From e3d24c07165354ac8c28ef75acefc521b5402bd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Schn=C3=BCll?= Date: Sat, 22 Oct 2022 23:29:37 +0200 Subject: [PATCH 001/714] initial working wip includes already some basic functions to display infos as well as some parts of the create substructure including the feature of matrix stores templates --- next.config.js | 3 + pages/{explore.js => explore/index.js} | 33 ++++- pages/explore/infoOverlay.js | 93 ++++++++++++++ pages/explore/moderateOverlay.js | 162 +++++++++++++++++++++++++ 4 files changed, 287 insertions(+), 4 deletions(-) rename pages/{explore.js => explore/index.js} (73%) create mode 100644 pages/explore/infoOverlay.js create mode 100644 pages/explore/moderateOverlay.js diff --git a/next.config.js b/next.config.js index 3c748d79..e928a953 100644 --- a/next.config.js +++ b/next.config.js @@ -18,6 +18,9 @@ module.exports = { }, }, contextRootSpaceRoomId: '!gzsKJXOMipzIxsoqYk:dev.medienhaus.udk-berlin.de', + templates: { + context: '!DKzCQzwwAaeXIVkLRh:dev.medienhaus.udk-berlin.de', + }, account: { allowAddingNewEmails: true, }, diff --git a/pages/explore.js b/pages/explore/index.js similarity index 73% rename from pages/explore.js rename to pages/explore/index.js index fa50d5c3..e3ead07b 100644 --- a/pages/explore.js +++ b/pages/explore/index.js @@ -3,19 +3,31 @@ import styled from 'styled-components'; import getConfig from 'next/config'; import { useTranslation } from 'react-i18next'; -import ContextMultiLevelSelect from '../components/ContextMultiLevelSelect'; -import { useAuth } from '../lib/Auth'; +import ContextMultiLevelSelect from '../../components/ContextMultiLevelSelect'; +import { useAuth } from '../../lib/Auth'; +import ModerateOverlay from './moderateOverlay'; +import InfoOverlay from './infoOverlay'; const ExploreSection = styled.div` & > * + * { margin-top: var(--margin); } - & > select + select { margin-top: calc(var(--margin) * 0.65); } `; +const ActionsSection = styled.div` + & { + margin-bottom: var(--margin); + } + + & > button { + margin-bottom: var(--margin); + margin-top: var(--margin); + } +`; + export default function Explore() { const auth = useAuth(); const { t } = useTranslation('explore'); @@ -23,6 +35,8 @@ export default function Explore() { const [activeContexts, setActiveContexts] = useState([getConfig().publicRuntimeConfig.contextRootSpaceRoomId]); const [contents, setContents] = useState(null); + const [showActions,setShowActions] = useState({modify:false,infos:false}); + useEffect(() => { const fetchContents = async () => { const contents = []; @@ -51,6 +65,8 @@ export default function Explore() { return ( <>

/explore

+

{activeContexts[activeContexts.length - 1]}

+ { (contents && contents.length > 0) ? ( @@ -71,6 +87,15 @@ export default function Explore() {

- { t('There are no contents for this context') } -

) }
+ + + + {showActions.infos && } + + + {showActions.modify && } + + ); -} +} \ No newline at end of file diff --git a/pages/explore/infoOverlay.js b/pages/explore/infoOverlay.js new file mode 100644 index 00000000..c7147070 --- /dev/null +++ b/pages/explore/infoOverlay.js @@ -0,0 +1,93 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import styled from 'styled-components'; +import getConfig from 'next/config'; +import _ from 'lodash'; + +import { useAuth } from '../../lib/Auth'; +import { useMatrix } from '../../lib/Matrix'; + + + + +const InfoOverlay = ({currentId }) => { + + const auth = useAuth(); + const matrix = auth.getAuthenticationProvider('matrix') + const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); + + + useEffect(() => { + getStateEvents(currentId) + + }, [currentId]); + + + const [stateEventInformation,setStateEventInformation] = useState({}) + + + async function getStateEvents(roomId) { // gets the stateevents of the room + const stateEvents = await matrixClient.roomState(roomId).catch((e) => { }) + const metaEvent = _.find(stateEvents,{ type: 'dev.medienhaus.meta' })?.content + const nameEvent = _.find(stateEvents,{ type: 'm.room.name' })?.content + const joinRulesEvent = _.find(stateEvents,{ type: 'dev.medienhaus.meta' })?.content + const memberEvent = _.find(stateEvents,{ type: 'm.room.member' })?.content + const topicEvent = _.find(stateEvents,{ type: 'm.room.topic' })?.content + + const initial = {name: nameEvent?.name, topic: topicEvent?.content} + const meta = {template: metaEvent?.template, type : metaEvent?.type, application: metaEvent?.application} + + const stateInformations = {initial : initial, meta : meta} + + setStateEventInformation({ ...stateEventInformation, meta: stateInformations.meta,initial: stateInformations.initial }) + + + } + + + + return ( + <> +

Name:{stateEventInformation?.initial?.name}

+

Topic:{stateEventInformation?.initial?.topic}

+
+ more +

Id: {currentId}

+
+ meta +
+
Type
+
{stateEventInformation?.meta?.type}
+
Template
+
{stateEventInformation?.meta?.template}
+
Application
+
{stateEventInformation?.meta?.type}
+
+
+
+ Institutions +
+
+ Members +
+
+ Referenced +
+
+
Join Rules
+
{stateEventInformation?.join}
+
Visibility
+
{stateEventInformation?.visibilty}
+
+
+ + + + + + + + + ); +}; + +export default InfoOverlay; diff --git a/pages/explore/moderateOverlay.js b/pages/explore/moderateOverlay.js new file mode 100644 index 00000000..c3c12abe --- /dev/null +++ b/pages/explore/moderateOverlay.js @@ -0,0 +1,162 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import styled from 'styled-components'; +import getConfig from 'next/config'; +import _ from 'lodash'; + +import { useAuth } from '../../lib/Auth'; +import { useMatrix } from '../../lib/Matrix'; + +const ModifySection = styled.details` + & { + margin-bottom: var(--margin); + } +`; + +const CreateSubstructureSection = styled.details` + & { + margin-bottom: var(--margin); + } +`; + + + +const ModerateOverlay = ({currentId }) => { + const auth = useAuth(); + const matrix = auth.getAuthenticationProvider('matrix') + const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); + + + // Logics for the functions of the 'CreateSubstructureSection' + // includes some basic input validation and dynamic form adjustments for the template selection + + const [contextTemplates, setContextTemplates] = useState([]); + + const [generateNewTemplate,setGenerateNewTemplate] = useState(false) + + const [createNew,setCreateNew] = useState({name:"",template:"",parent:"",interfaceError:""}) //stores all nessesarry user input from the form + + async function getTemplatesOfContexts(roomId) { // gets the templates from the + if(contextTemplates.length > 0) return + const metaEvent = await auth.getAuthenticationProvider('matrix').getMatrixClient().getStateEvent(roomId, 'dev.medienhaus.meta').catch(() => {}); + if((metaEvent?.template !== 'templates')) return + console.log(metaEvent) + const roomContent = await (matrix.fetchRoomMessages(roomId,5)); + const templates = _.uniq(roomContent?.chunk.map(entry => entry?.content?.body)).filter(e => e); + setContextTemplates(templates) + } + + + + + + useEffect(() => { // basic input validation for the input fields to create a new substructure + + if (createNew.name === "" || createNew?.name.length < 4) { + createNew.interfaceError = "name too short" + return; + } else { + createNew.interfaceError = ""; + } + + if (createNew.template === "" || createNew?.template.length < 4) { + createNew.interfaceError = "template name too short" + return; + } else { + createNew.interfaceError = ""; + } + + }, [createNew]); + + const createContext = (e) => { + e.preventDefault(); + if(currentId.length > 10 && currentId.charAt(0) === '!' && currentId.includes(':')) { //just some really really basic check if it could be an matrix id + setCreateNew({ ...createNew, parent: currentId }); + console.log("created") + } else { + createNew.interfaceError = "something went wrong with the selected matrix Id, please reload" + } + + } + + const createNewChangeHandle = (e) => { + if(e.target.name === "template" && e.target.value === "_createNew") {//check if dropdown is selected for new to modify form + e.target.value === '_createNew'? setGenerateNewTemplate(true) : setGenerateNewTemplate(false) + setCreateNew({ ...createNew, template: "" }) + return; + } + if(e.target.name === "newTemplate") { + setCreateNew({ ...createNew, template: e.target.value }); + } else { + setCreateNew({ ...createNew, [e.target.name]: e.target.value }); //this is the regular way if no errors occured before + } + }; + + return ( + <> + + Modify + + + +
+ members +
+
+ advanced + + + +
+
+ danger zone + +
+ +
+ getTemplatesOfContexts(getConfig().publicRuntimeConfig.templates.context)}> {/* will only load the templates after expanding this view to prevent unnecessary network traffic */} + create Substructure +
+ + + {generateNewTemplate && }{/* this input is only generated if the dropwdown was selected 'create new…' */} + + +
+ advanced + + + +
+ + {createNew.interfaceError &&

‼️ {createNew.interfaceError}

} {/* Showing the current Error to the user if some input validation failed */} + +
+
+ + + + + ); +}; + +export default ModerateOverlay; From 02abea652ba1ab9cdeddecaa265600c77f46bebe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Schn=C3=BCll?= Date: Sun, 23 Oct 2022 01:34:25 +0200 Subject: [PATCH 002/714] adding members to info --- pages/explore/infoOverlay.js | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/pages/explore/infoOverlay.js b/pages/explore/infoOverlay.js index c7147070..91752613 100644 --- a/pages/explore/infoOverlay.js +++ b/pages/explore/infoOverlay.js @@ -30,15 +30,24 @@ const InfoOverlay = ({currentId }) => { const metaEvent = _.find(stateEvents,{ type: 'dev.medienhaus.meta' })?.content const nameEvent = _.find(stateEvents,{ type: 'm.room.name' })?.content const joinRulesEvent = _.find(stateEvents,{ type: 'dev.medienhaus.meta' })?.content - const memberEvent = _.find(stateEvents,{ type: 'm.room.member' })?.content + const memberEvent = _.filter(stateEvents,{ type: 'm.room.member' }) const topicEvent = _.find(stateEvents,{ type: 'm.room.topic' })?.content - const initial = {name: nameEvent?.name, topic: topicEvent?.content} - const meta = {template: metaEvent?.template, type : metaEvent?.type, application: metaEvent?.application} + const members = _.omitBy(_.map(memberEvent,member => { + if(member?.content.membership === 'join'){ + return {id:member?.sender,displaname:member?.content?.displayname} + } + }), _.isNil) + const institutions = _.omitBy(_.map(members, member => member?.id.split(':')[1]), _.isNil) - const stateInformations = {initial : initial, meta : meta} - setStateEventInformation({ ...stateEventInformation, meta: stateInformations.meta,initial: stateInformations.initial }) + const initial = {name: nameEvent?.name, topic: topicEvent?.content,members:members} + const custom = {template: metaEvent?.template, type : metaEvent?.type, application: metaEvent?.application} + + const stateInformations = {initial : initial, custom : custom} + + setStateEventInformation({ ...stateEventInformation, custom: stateInformations.custom, initial: stateInformations.initial }) + } @@ -56,18 +65,29 @@ const InfoOverlay = ({currentId }) => { meta
Type
-
{stateEventInformation?.meta?.type}
+
{stateEventInformation?.custom?.type}
Template
-
{stateEventInformation?.meta?.template}
+
{stateEventInformation?.custom?.template}
Application
-
{stateEventInformation?.meta?.type}
+
{stateEventInformation?.custom?.type}
Institutions +

{stateEventInformation?.custom?.institutions}

Members +
    + {_.map(stateEventInformation?.initial?.members, (member) => { + return
    + {member?.displaname} +

    send dm

    +

    invite to…

    +
    + }) + } +
Referenced From a1c38e1ca6c0d7470d916362d8e6bd2adfdef2ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Schn=C3=BCll?= Date: Sun, 23 Oct 2022 19:16:31 +0200 Subject: [PATCH 003/714] restructure and rename to actions --- pages/explore/actions.js | 47 +++++++++++ pages/explore/addOverlay.js | 51 ++++++++++++ pages/explore/index.js | 28 ++----- pages/explore/infoOverlay.js | 132 +++++++++++++++--------------- pages/explore/moderateOverlay.js | 133 ++++++++++++++----------------- 5 files changed, 232 insertions(+), 159 deletions(-) create mode 100644 pages/explore/actions.js create mode 100644 pages/explore/addOverlay.js diff --git a/pages/explore/actions.js b/pages/explore/actions.js new file mode 100644 index 00000000..c7868229 --- /dev/null +++ b/pages/explore/actions.js @@ -0,0 +1,47 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import styled from 'styled-components'; +import getConfig from 'next/config'; +import _ from 'lodash'; + +import { useAuth } from '../../lib/Auth'; +import { useMatrix } from '../../lib/Matrix'; +import ModerateOverlay from './moderateOverlay'; +import InfoOverlay from './infoOverlay'; +import AddOverlay from './addOverlay'; + + +const ActionsSection = styled.div` + & { + margin-bottom: var(--margin); + } + + & > button { + margin-bottom: var(--margin); + margin-top: var(--margin); + } +`; + +const Actions = ({ activeContexts }) => { + const auth = useAuth(); + const matrix = auth.getAuthenticationProvider('matrix'); + const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); + + const [showActions, setShowActions] = useState({ modify: false, infos: false, add: false }); + + return ( + + + { showActions.infos && } + + + { showActions.add && } + + + { showActions.modify && } + + + + ); +}; + +export default Actions; diff --git a/pages/explore/addOverlay.js b/pages/explore/addOverlay.js new file mode 100644 index 00000000..fa343003 --- /dev/null +++ b/pages/explore/addOverlay.js @@ -0,0 +1,51 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import styled from 'styled-components'; +import getConfig from 'next/config'; +import _ from 'lodash'; + +import { useAuth } from '../../lib/Auth'; +import { useMatrix } from '../../lib/Matrix'; + +const ModSection = styled.details` + & { + margin-bottom: var(--margin); + } + & > button { + margin-bottom: var(--margin); + } +`; + +const UserSection = styled.details` + & { + margin-bottom: var(--margin); + } + + & > button { + margin-bottom: var(--margin); + } +`; + +const AddOverlay = ({ currentId }) => { + const auth = useAuth(); + const matrix = auth.getAuthenticationProvider('matrix'); + const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); + + return ( + <> + + User Section + + + + + Mod Section + + + + + + + ); +}; + +export default AddOverlay; diff --git a/pages/explore/index.js b/pages/explore/index.js index e3ead07b..77da66c6 100644 --- a/pages/explore/index.js +++ b/pages/explore/index.js @@ -7,6 +7,8 @@ import ContextMultiLevelSelect from '../../components/ContextMultiLevelSelect'; import { useAuth } from '../../lib/Auth'; import ModerateOverlay from './moderateOverlay'; import InfoOverlay from './infoOverlay'; +import AddOverlay from './addOverlay'; +import Actions from './actions' const ExploreSection = styled.div` & > * + * { @@ -17,16 +19,7 @@ const ExploreSection = styled.div` } `; -const ActionsSection = styled.div` - & { - margin-bottom: var(--margin); - } - & > button { - margin-bottom: var(--margin); - margin-top: var(--margin); - } -`; export default function Explore() { const auth = useAuth(); @@ -35,7 +28,6 @@ export default function Explore() { const [activeContexts, setActiveContexts] = useState([getConfig().publicRuntimeConfig.contextRootSpaceRoomId]); const [contents, setContents] = useState(null); - const [showActions,setShowActions] = useState({modify:false,infos:false}); useEffect(() => { const fetchContents = async () => { @@ -65,8 +57,8 @@ export default function Explore() { return ( <>

/explore

-

{activeContexts[activeContexts.length - 1]}

- +

{ activeContexts[activeContexts.length - 1] }

+ { (contents && contents.length > 0) ? ( @@ -88,14 +80,10 @@ export default function Explore() { ) } - - - {showActions.infos && } - - - {showActions.modify && } + + - + ); -} \ No newline at end of file +} diff --git a/pages/explore/infoOverlay.js b/pages/explore/infoOverlay.js index 91752613..81db4adc 100644 --- a/pages/explore/infoOverlay.js +++ b/pages/explore/infoOverlay.js @@ -6,107 +6,105 @@ import _ from 'lodash'; import { useAuth } from '../../lib/Auth'; import { useMatrix } from '../../lib/Matrix'; - - - -const InfoOverlay = ({currentId }) => { - +const InfoOverlay = ({ currentId }) => { const auth = useAuth(); - const matrix = auth.getAuthenticationProvider('matrix') + const matrix = auth.getAuthenticationProvider('matrix'); const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); - - useEffect(() => { - getStateEvents(currentId) - + useEffect(() => { + getStateEvents(currentId); }, [currentId]); - - const [stateEventInformation,setStateEventInformation] = useState({}) - + const [stateEventInformation, setStateEventInformation] = useState({}); async function getStateEvents(roomId) { // gets the stateevents of the room - const stateEvents = await matrixClient.roomState(roomId).catch((e) => { }) - const metaEvent = _.find(stateEvents,{ type: 'dev.medienhaus.meta' })?.content - const nameEvent = _.find(stateEvents,{ type: 'm.room.name' })?.content - const joinRulesEvent = _.find(stateEvents,{ type: 'dev.medienhaus.meta' })?.content - const memberEvent = _.filter(stateEvents,{ type: 'm.room.member' }) - const topicEvent = _.find(stateEvents,{ type: 'm.room.topic' })?.content - - const members = _.omitBy(_.map(memberEvent,member => { - if(member?.content.membership === 'join'){ - return {id:member?.sender,displaname:member?.content?.displayname} - } - }), _.isNil) - const institutions = _.omitBy(_.map(members, member => member?.id.split(':')[1]), _.isNil) - - - const initial = {name: nameEvent?.name, topic: topicEvent?.content,members:members} - const custom = {template: metaEvent?.template, type : metaEvent?.type, application: metaEvent?.application} - - const stateInformations = {initial : initial, custom : custom} - - setStateEventInformation({ ...stateEventInformation, custom: stateInformations.custom, initial: stateInformations.initial }) - - - + const stateEvents = await matrixClient.roomState(roomId).catch((e) => { }); + const metaEvent = _.find(stateEvents, { type: 'dev.medienhaus.meta' })?.content; + const nameEvent = _.find(stateEvents, { type: 'm.room.name' })?.content; + const joinRulesEvent = _.find(stateEvents, { type: 'dev.medienhaus.meta' })?.content; + const memberEvent = _.filter(stateEvents, { type: 'm.room.member' }); + const topicEvent = _.find(stateEvents, { type: 'm.room.topic' })?.content; + + const members = _.filter( + _.map(memberEvent, member => { + return { id: member?.sender, displaname: member?.content?.displayname }; + }), + el => !_.isNull(el)); //filter out empty ones + + const institutions = _.uniq(_.map(members, member => member?.id.split(':')[1])); + + console.log(stateEvents); + + const initial = { name: nameEvent?.name, + topic: topicEvent?.content, + members: members, + }; + const custom = { + template: metaEvent?.template, + type: metaEvent?.type, + application: metaEvent?.application, + institutions: institutions, + }; + + const stateInformations = { initial: initial, custom: custom }; + + setStateEventInformation({ ...stateEventInformation, custom: stateInformations.custom, initial: stateInformations.initial }); } - - return ( <> -

Name:{stateEventInformation?.initial?.name}

-

Topic:{stateEventInformation?.initial?.topic}

+

Name:{ stateEventInformation?.initial?.name }

+

Topic:{ stateEventInformation?.initial?.topic }

more -

Id: {currentId}

+

Id: { currentId }

meta
+
Application
+
{ stateEventInformation?.custom?.type }
Type
-
{stateEventInformation?.custom?.type}
+
{ stateEventInformation?.custom?.type }
Template
-
{stateEventInformation?.custom?.template}
-
Application
-
{stateEventInformation?.custom?.type}
+
{ stateEventInformation?.custom?.template }
Institutions -

{stateEventInformation?.custom?.institutions}

+
    + { _.map(stateEventInformation?.custom?.institutions, institution => { + return
  • { institution }
  • ; + }) } +
+
Members
    - {_.map(stateEventInformation?.initial?.members, (member) => { - return
    - {member?.displaname} -

    send dm

    -

    invite to…

    -
    - }) - } + { _.map(stateEventInformation?.initial?.members, (member) => { + return
  • +
    + { member?.displaname ? member?.displaname : member?.id.split(':')[0].substring(1) } { /* If Displayname is not set fallback to user id */ } +

    send dm

    +

    invite to…

    +
    +
  • ; + }) + }
Referenced
-
Join Rules
-
{stateEventInformation?.join}
-
Visibility
-
{stateEventInformation?.visibilty}
-
+
Join Rules
+
{ stateEventInformation?.join }
+
Visibility
+
{ stateEventInformation?.visibilty }
+
- - - - - - - + ); }; diff --git a/pages/explore/moderateOverlay.js b/pages/explore/moderateOverlay.js index c3c12abe..9a9cc7c3 100644 --- a/pages/explore/moderateOverlay.js +++ b/pages/explore/moderateOverlay.js @@ -18,73 +18,63 @@ const CreateSubstructureSection = styled.details` } `; - - -const ModerateOverlay = ({currentId }) => { +const ModerateOverlay = ({ currentId }) => { const auth = useAuth(); - const matrix = auth.getAuthenticationProvider('matrix') + const matrix = auth.getAuthenticationProvider('matrix'); const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); - // Logics for the functions of the 'CreateSubstructureSection' // includes some basic input validation and dynamic form adjustments for the template selection const [contextTemplates, setContextTemplates] = useState([]); - const [generateNewTemplate,setGenerateNewTemplate] = useState(false) + const [generateNewTemplate, setGenerateNewTemplate] = useState(false); - const [createNew,setCreateNew] = useState({name:"",template:"",parent:"",interfaceError:""}) //stores all nessesarry user input from the form + const [createNew, setCreateNew] = useState({ name: '', template: '', parent: '', interfaceError: '' }); //stores all nessesarry user input from the form - async function getTemplatesOfContexts(roomId) { // gets the templates from the - if(contextTemplates.length > 0) return + async function getTemplatesOfContexts(roomId) { // gets the templates from the + if (contextTemplates.length > 0) return; const metaEvent = await auth.getAuthenticationProvider('matrix').getMatrixClient().getStateEvent(roomId, 'dev.medienhaus.meta').catch(() => {}); - if((metaEvent?.template !== 'templates')) return - console.log(metaEvent) - const roomContent = await (matrix.fetchRoomMessages(roomId,5)); - const templates = _.uniq(roomContent?.chunk.map(entry => entry?.content?.body)).filter(e => e); - setContextTemplates(templates) + if ((metaEvent?.template !== 'templates')) return; + console.log(metaEvent); + const roomContent = await (matrix.fetchRoomMessages(roomId, 5)); + const templates = _.uniq(roomContent?.chunk.map(entry => entry?.content?.body)).filter(e => e); + setContextTemplates(templates); } - - - - useEffect(() => { // basic input validation for the input fields to create a new substructure - - if (createNew.name === "" || createNew?.name.length < 4) { - createNew.interfaceError = "name too short" + if (createNew.name === '' || createNew?.name.length < 4) { + createNew.interfaceError = 'name too short'; return; } else { - createNew.interfaceError = ""; + createNew.interfaceError = ''; } - - if (createNew.template === "" || createNew?.template.length < 4) { - createNew.interfaceError = "template name too short" - return; - } else { - createNew.interfaceError = ""; - } + if (createNew.template === '' || createNew?.template.length < 4) { + createNew.interfaceError = 'template name too short'; + return; + } else { + createNew.interfaceError = ''; + } }, [createNew]); const createContext = (e) => { e.preventDefault(); - if(currentId.length > 10 && currentId.charAt(0) === '!' && currentId.includes(':')) { //just some really really basic check if it could be an matrix id + if (currentId.length > 10 && currentId.charAt(0) === '!' && currentId.includes(':')) { //just some really really basic check if it could be an matrix id setCreateNew({ ...createNew, parent: currentId }); - console.log("created") + console.log('created'); } else { - createNew.interfaceError = "something went wrong with the selected matrix Id, please reload" + createNew.interfaceError = 'something went wrong with the selected matrix Id, please reload'; } - - } + }; const createNewChangeHandle = (e) => { - if(e.target.name === "template" && e.target.value === "_createNew") {//check if dropdown is selected for new to modify form - e.target.value === '_createNew'? setGenerateNewTemplate(true) : setGenerateNewTemplate(false) - setCreateNew({ ...createNew, template: "" }) + if (e.target.name === 'template' && e.target.value === '_createNew') {//check if dropdown is selected for new to modify form + e.target.value === '_createNew'? setGenerateNewTemplate(true) : setGenerateNewTemplate(false); + setCreateNew({ ...createNew, template: '' }); return; - } - if(e.target.name === "newTemplate") { + } + if (e.target.name === 'newTemplate') { setCreateNew({ ...createNew, template: e.target.value }); } else { setCreateNew({ ...createNew, [e.target.name]: e.target.value }); //this is the regular way if no errors occured before @@ -95,67 +85,66 @@ const ModerateOverlay = ({currentId }) => { <> Modify - - - + + +
members
- advanced - - - + advanced + + +
danger zone
- +
- getTemplatesOfContexts(getConfig().publicRuntimeConfig.templates.context)}> {/* will only load the templates after expanding this view to prevent unnecessary network traffic */} + getTemplatesOfContexts(getConfig().publicRuntimeConfig.templates.context)}> { /* will only load the templates after expanding this view to prevent unnecessary network traffic */ } create Substructure
- + - {generateNewTemplate && }{/* this input is only generated if the dropwdown was selected 'create new…' */} - + { generateNewTemplate && }{ /* this input is only generated if the dropwdown was selected 'create new…' */ }
advanced
- - {createNew.interfaceError &&

‼️ {createNew.interfaceError}

} {/* Showing the current Error to the user if some input validation failed */} - + + { createNew.interfaceError &&

‼️ { createNew.interfaceError }

} { /* Showing the current Error to the user if some input validation failed */ } +
- - + ); }; From 33c995fbdf9ad28203b5799b641933a001fe0b9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Schn=C3=BCll?= Date: Sun, 23 Oct 2022 20:10:24 +0200 Subject: [PATCH 004/714] restructuring buttons --- pages/explore/actions.js | 59 +++++++++++++++---- pages/explore/{addOverlay.js => addAction.js} | 4 +- pages/explore/index.js | 16 ++--- .../explore/{infoOverlay.js => infoAction.js} | 37 +++++++----- .../{moderateOverlay.js => settingsAction.js} | 4 +- 5 files changed, 77 insertions(+), 43 deletions(-) rename pages/explore/{addOverlay.js => addAction.js} (94%) rename pages/explore/{infoOverlay.js => infoAction.js} (83%) rename pages/explore/{moderateOverlay.js => settingsAction.js} (98%) diff --git a/pages/explore/actions.js b/pages/explore/actions.js index c7868229..868c257d 100644 --- a/pages/explore/actions.js +++ b/pages/explore/actions.js @@ -5,39 +5,72 @@ import _ from 'lodash'; import { useAuth } from '../../lib/Auth'; import { useMatrix } from '../../lib/Matrix'; -import ModerateOverlay from './moderateOverlay'; -import InfoOverlay from './infoOverlay'; -import AddOverlay from './addOverlay'; +import SettingsAction from './settingsAction'; +import InfoAction from './infoAction'; +import AddAction from './addAction'; - -const ActionsSection = styled.div` +const ActionsSection = styled.details` & { margin-bottom: var(--margin); + } - & > button { + & > div > button { margin-bottom: var(--margin); margin-top: var(--margin); + margin-right: var(--margin); + width: 50px; + + } + +`; + +const ButtonsSection = styled.div` + & { + margin-bottom: var(--margin); + } `; -const Actions = ({ activeContexts }) => { +const MenuSection = styled.div` + & { + margin-bottom: var(--margin); + + } +`; + +const Actions = ({ currentId }) => { const auth = useAuth(); const matrix = auth.getAuthenticationProvider('matrix'); const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); const [showActions, setShowActions] = useState({ modify: false, infos: false, add: false }); + const [stateEvents, setStateEvents] = useState(); + + useEffect(() => { + getStateEvents(currentId); + }, [currentId]); + + async function getStateEvents(roomId) { // gets the stateevents of the room + setStateEvents(await matrixClient.roomState(roomId).catch((e) => { })); + } + return ( - - { showActions.infos && } + getStateEvents(currentId)}>… - - { showActions.add && } + + + + + - - { showActions.modify && } + + { showActions.infos && } + { showActions.add && } + { showActions.settings && } + diff --git a/pages/explore/addOverlay.js b/pages/explore/addAction.js similarity index 94% rename from pages/explore/addOverlay.js rename to pages/explore/addAction.js index fa343003..6a5926c1 100644 --- a/pages/explore/addOverlay.js +++ b/pages/explore/addAction.js @@ -25,7 +25,7 @@ const UserSection = styled.details` } `; -const AddOverlay = ({ currentId }) => { +const AddAction = ({ currentId }) => { const auth = useAuth(); const matrix = auth.getAuthenticationProvider('matrix'); const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); @@ -48,4 +48,4 @@ const AddOverlay = ({ currentId }) => { ); }; -export default AddOverlay; +export default AddAction; diff --git a/pages/explore/index.js b/pages/explore/index.js index 77da66c6..048925ee 100644 --- a/pages/explore/index.js +++ b/pages/explore/index.js @@ -5,10 +5,7 @@ import { useTranslation } from 'react-i18next'; import ContextMultiLevelSelect from '../../components/ContextMultiLevelSelect'; import { useAuth } from '../../lib/Auth'; -import ModerateOverlay from './moderateOverlay'; -import InfoOverlay from './infoOverlay'; -import AddOverlay from './addOverlay'; -import Actions from './actions' +import Actions from './actions'; const ExploreSection = styled.div` & > * + * { @@ -19,8 +16,6 @@ const ExploreSection = styled.div` } `; - - export default function Explore() { const auth = useAuth(); const { t } = useTranslation('explore'); @@ -28,7 +23,6 @@ export default function Explore() { const [activeContexts, setActiveContexts] = useState([getConfig().publicRuntimeConfig.contextRootSpaceRoomId]); const [contents, setContents] = useState(null); - useEffect(() => { const fetchContents = async () => { const contents = []; @@ -64,8 +58,8 @@ export default function Explore() { { (contents && contents.length > 0) ? (
    - { contents.map((content) => ( -
  1. + { contents.map((content, key) => ( +
  2. { content.name } { content.type && (<> (type: { content.type })) } @@ -80,10 +74,8 @@ export default function Explore() { ) } - - + - ); } diff --git a/pages/explore/infoOverlay.js b/pages/explore/infoAction.js similarity index 83% rename from pages/explore/infoOverlay.js rename to pages/explore/infoAction.js index 81db4adc..774c1d78 100644 --- a/pages/explore/infoOverlay.js +++ b/pages/explore/infoAction.js @@ -6,19 +6,28 @@ import _ from 'lodash'; import { useAuth } from '../../lib/Auth'; import { useMatrix } from '../../lib/Matrix'; -const InfoOverlay = ({ currentId }) => { + +const InfoSection = styled.div` + & { + margin-bottom: var(--margin); + + } + + +`; + +const InfoAction = ({ currentId, stateEvents }) => { const auth = useAuth(); const matrix = auth.getAuthenticationProvider('matrix'); const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); useEffect(() => { - getStateEvents(currentId); - }, [currentId]); + processStateEvents(stateEvents); + }, [stateEvents]); const [stateEventInformation, setStateEventInformation] = useState({}); - async function getStateEvents(roomId) { // gets the stateevents of the room - const stateEvents = await matrixClient.roomState(roomId).catch((e) => { }); + async function processStateEvents(stateEvents) { // gets the stateevents of the room const metaEvent = _.find(stateEvents, { type: 'dev.medienhaus.meta' })?.content; const nameEvent = _.find(stateEvents, { type: 'm.room.name' })?.content; const joinRulesEvent = _.find(stateEvents, { type: 'dev.medienhaus.meta' })?.content; @@ -52,9 +61,9 @@ const InfoOverlay = ({ currentId }) => { } return ( - <> -

    Name:{ stateEventInformation?.initial?.name }

    -

    Topic:{ stateEventInformation?.initial?.topic }

    + + { stateEventInformation?.initial?.name ?

    Name:{ stateEventInformation?.initial?.name }

    : <> } + { stateEventInformation?.initial?.topic ?

    Topic:{ stateEventInformation?.initial?.topic }

    : <> }
    more

    Id: { currentId }

    @@ -72,8 +81,8 @@ const InfoOverlay = ({ currentId }) => {
    Institutions
      - { _.map(stateEventInformation?.custom?.institutions, institution => { - return
    • { institution }
    • ; + { _.map(stateEventInformation?.custom?.institutions, (institution, key) => { + return
    • { institution }
    • ; }) }
    @@ -81,8 +90,8 @@ const InfoOverlay = ({ currentId }) => {
    Members
      - { _.map(stateEventInformation?.initial?.members, (member) => { - return
    • + { _.map(stateEventInformation?.initial?.members, (member, key) => { + return
    • { member?.displaname ? member?.displaname : member?.id.split(':')[0].substring(1) } { /* If Displayname is not set fallback to user id */ }

      send dm

      @@ -103,9 +112,9 @@ const InfoOverlay = ({ currentId }) => {
      { stateEventInformation?.visibilty }
      - + ); }; -export default InfoOverlay; +export default InfoAction; diff --git a/pages/explore/moderateOverlay.js b/pages/explore/settingsAction.js similarity index 98% rename from pages/explore/moderateOverlay.js rename to pages/explore/settingsAction.js index 9a9cc7c3..aad1baf6 100644 --- a/pages/explore/moderateOverlay.js +++ b/pages/explore/settingsAction.js @@ -18,7 +18,7 @@ const CreateSubstructureSection = styled.details` } `; -const ModerateOverlay = ({ currentId }) => { +const SettingsAction = ({ currentId }) => { const auth = useAuth(); const matrix = auth.getAuthenticationProvider('matrix'); const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); @@ -148,4 +148,4 @@ const ModerateOverlay = ({ currentId }) => { ); }; -export default ModerateOverlay; +export default SettingsAction; From 7cbe49ad8e1f537fd236c6946c995a7ee4b0f1d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Schn=C3=BCll?= Date: Sun, 23 Oct 2022 20:53:53 +0200 Subject: [PATCH 005/714] checking for mod rights --- pages/explore/actions.js | 13 ++++++++++--- pages/explore/addAction.js | 20 +++++++++---------- pages/explore/infoAction.js | 38 ++++++++++++++++++++++++------------- 3 files changed, 45 insertions(+), 26 deletions(-) diff --git a/pages/explore/actions.js b/pages/explore/actions.js index 868c257d..aea0b511 100644 --- a/pages/explore/actions.js +++ b/pages/explore/actions.js @@ -48,12 +48,19 @@ const Actions = ({ currentId }) => { const [stateEvents, setStateEvents] = useState(); + const [userInfos, setUserInfos] = useState({}); //stores information about the curren User + useEffect(() => { getStateEvents(currentId); }, [currentId]); async function getStateEvents(roomId) { // gets the stateevents of the room - setStateEvents(await matrixClient.roomState(roomId).catch((e) => { })); + const events = await matrixClient.roomState(roomId).catch((e) => { }); + setStateEvents(events); + const userId = matrixClient?.credentials?.userId; + const powerLevelsEvent = _.find(events, { type: 'm.room.power_levels' })?.content; + const modRights = powerLevelsEvent?.users[userId] >= 50; //check if the current user got is listed with a custom power level if true and >= 50 (mod default) mod flag is set true + setUserInfos({ ...userInfos, id: userId, mod: modRights }); } return ( @@ -63,12 +70,12 @@ const Actions = ({ currentId }) => { - + { showActions.infos && } - { showActions.add && } + { showActions.add && } { showActions.settings && } diff --git a/pages/explore/addAction.js b/pages/explore/addAction.js index 6a5926c1..530372f8 100644 --- a/pages/explore/addAction.js +++ b/pages/explore/addAction.js @@ -6,7 +6,7 @@ import _ from 'lodash'; import { useAuth } from '../../lib/Auth'; import { useMatrix } from '../../lib/Matrix'; -const ModSection = styled.details` +const ModSection = styled.div` & { margin-bottom: var(--margin); } @@ -15,7 +15,7 @@ const ModSection = styled.details` } `; -const UserSection = styled.details` +const UserSection = styled.div` & { margin-bottom: var(--margin); } @@ -25,7 +25,7 @@ const UserSection = styled.details` } `; -const AddAction = ({ currentId }) => { +const AddAction = ({ currentId, userInfos }) => { const auth = useAuth(); const matrix = auth.getAuthenticationProvider('matrix'); const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); @@ -33,16 +33,16 @@ const AddAction = ({ currentId }) => { return ( <> - User Section - - Mod Section - - - - + { userInfos?.mod? //if no mod rights are granted for this current Id this section will not be displayed + + + + + : <> + } ); diff --git a/pages/explore/infoAction.js b/pages/explore/infoAction.js index 774c1d78..f97024a0 100644 --- a/pages/explore/infoAction.js +++ b/pages/explore/infoAction.js @@ -6,7 +6,6 @@ import _ from 'lodash'; import { useAuth } from '../../lib/Auth'; import { useMatrix } from '../../lib/Matrix'; - const InfoSection = styled.div` & { margin-bottom: var(--margin); @@ -34,15 +33,13 @@ const InfoAction = ({ currentId, stateEvents }) => { const memberEvent = _.filter(stateEvents, { type: 'm.room.member' }); const topicEvent = _.find(stateEvents, { type: 'm.room.topic' })?.content; - const members = _.filter( + const members = _.compact( //filter out empty ones _.map(memberEvent, member => { + if (member?.content?.membership === 'leave') return; //check if the latest event was an leave, so the user is not a member anymore at this point return { id: member?.sender, displaname: member?.content?.displayname }; - }), - el => !_.isNull(el)); //filter out empty ones - - const institutions = _.uniq(_.map(members, member => member?.id.split(':')[1])); + })); - console.log(stateEvents); + const institutions = _.uniq(_.map(members, member => member?.id.split(':')[1])); //show only the tld's of the homeservers. '_.uniq' filters out duplicates const initial = { name: nameEvent?.name, topic: topicEvent?.content, @@ -64,6 +61,26 @@ const InfoAction = ({ currentId, stateEvents }) => { { stateEventInformation?.initial?.name ?

      Name:{ stateEventInformation?.initial?.name }

      : <> } { stateEventInformation?.initial?.topic ?

      Topic:{ stateEventInformation?.initial?.topic }

      : <> } +
      + { stateEventInformation?.initial?.name ? <> +
      Name
      +
      { stateEventInformation?.initial?.name }
      + + : <> } + { stateEventInformation?.initial?.topic ? <> +
      Topic
      +
      { stateEventInformation?.initial?.topic }
      + + : <> } + <> +
      Join Rules
      +
      { stateEventInformation?.join }
      + + <> +
      Visibility
      +
      { stateEventInformation?.visibilty }
      + +
      more

      Id: { currentId }

      @@ -105,12 +122,7 @@ const InfoAction = ({ currentId, stateEvents }) => {
      Referenced
      -
      -
      Join Rules
      -
      { stateEventInformation?.join }
      -
      Visibility
      -
      { stateEventInformation?.visibilty }
      -
      +
      From c14286d7c6ed07c5cc4debdf6481d93e74540259 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Schn=C3=BCll?= Date: Sun, 23 Oct 2022 21:25:52 +0200 Subject: [PATCH 006/714] adding additional information in infoAction --- pages/explore/actions.js | 6 ++-- pages/explore/infoAction.js | 61 +++++++++++++++++++++++++++++-------- 2 files changed, 51 insertions(+), 16 deletions(-) diff --git a/pages/explore/actions.js b/pages/explore/actions.js index aea0b511..c66aee64 100644 --- a/pages/explore/actions.js +++ b/pages/explore/actions.js @@ -48,7 +48,7 @@ const Actions = ({ currentId }) => { const [stateEvents, setStateEvents] = useState(); - const [userInfos, setUserInfos] = useState({}); //stores information about the curren User + const [userInfos, setUserInfos] = useState({mod:false}); //stores information about the curren User useEffect(() => { getStateEvents(currentId); @@ -66,7 +66,6 @@ const Actions = ({ currentId }) => { return ( getStateEvents(currentId)}>… - @@ -74,13 +73,12 @@ const Actions = ({ currentId }) => { - { showActions.infos && } + { showActions.infos && } { showActions.add && } { showActions.settings && } - ); }; diff --git a/pages/explore/infoAction.js b/pages/explore/infoAction.js index f97024a0..0173953a 100644 --- a/pages/explore/infoAction.js +++ b/pages/explore/infoAction.js @@ -14,8 +14,14 @@ const InfoSection = styled.div` `; - -const InfoAction = ({ currentId, stateEvents }) => { +/* +* @TODO: +* - adding routes for user interaction more -> members -> username +* - searching for referenced in sync cached rooms (fast) +* - searching for referenced in root tree (slow) +*/ + +const InfoAction = ({ currentId, stateEvents, userInfos }) => { const auth = useAuth(); const matrix = auth.getAuthenticationProvider('matrix'); const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); @@ -29,10 +35,13 @@ const InfoAction = ({ currentId, stateEvents }) => { async function processStateEvents(stateEvents) { // gets the stateevents of the room const metaEvent = _.find(stateEvents, { type: 'dev.medienhaus.meta' })?.content; const nameEvent = _.find(stateEvents, { type: 'm.room.name' })?.content; - const joinRulesEvent = _.find(stateEvents, { type: 'dev.medienhaus.meta' })?.content; + const joinRulesEvent = _.find(stateEvents, { type: 'm.room.join_rules' })?.content; + const historyVisibilityEvent = _.find(stateEvents, { type: 'm.room.history_visibility' })?.content; const memberEvent = _.filter(stateEvents, { type: 'm.room.member' }); const topicEvent = _.find(stateEvents, { type: 'm.room.topic' })?.content; + console.log(joinRulesEvent); + const members = _.compact( //filter out empty ones _.map(memberEvent, member => { if (member?.content?.membership === 'leave') return; //check if the latest event was an leave, so the user is not a member anymore at this point @@ -44,6 +53,8 @@ const InfoAction = ({ currentId, stateEvents }) => { const initial = { name: nameEvent?.name, topic: topicEvent?.content, members: members, + join: joinRulesEvent?.join_rule, + visibility: historyVisibilityEvent?.history_visibility }; const custom = { template: metaEvent?.template, @@ -59,8 +70,6 @@ const InfoAction = ({ currentId, stateEvents }) => { return ( - { stateEventInformation?.initial?.name ?

      Name:{ stateEventInformation?.initial?.name }

      : <> } - { stateEventInformation?.initial?.topic ?

      Topic:{ stateEventInformation?.initial?.topic }

      : <> }
      { stateEventInformation?.initial?.name ? <>
      Name
      @@ -74,11 +83,37 @@ const InfoAction = ({ currentId, stateEvents }) => { : <> } <>
      Join Rules
      -
      { stateEventInformation?.join }
      +
      { (() => { + switch (stateEventInformation?.initial?.join) { + case 'public': + return <>🌐; + case 'restricted': // is the case if is member is also member of a different specified room (aka spacemember function in element) + return <>🔐; + case 'knock': + return <>🚪; + case 'invite': + return <>🔒; + default: + return <>; + } + })() }
      <>
      Visibility
      -
      { stateEventInformation?.visibilty }
      +
      { (() => { + switch (stateEventInformation?.initial?.visibility) { + case 'world_readable': + return <>🌐; + case 'shared': + return <>📖; + case 'joined': + return <>🔐; + case 'invited': + return <>🔒; + default: + return <>; + } + })() }
      @@ -109,11 +144,13 @@ const InfoAction = ({ currentId, stateEvents }) => {
        { _.map(stateEventInformation?.initial?.members, (member, key) => { return
      • -
        - { member?.displaname ? member?.displaname : member?.id.split(':')[0].substring(1) } { /* If Displayname is not set fallback to user id */ } -

        send dm

        -

        invite to…

        -
        + { member?.id === userInfos?.id? <>{ member?.displaname ? member?.displaname : member?.id.split(':')[0].substring(1) } : //checks if the user is the logged in user, to disable interaction +
        + { member?.displaname ? member?.displaname : member?.id.split(':')[0].substring(1) } { /* If Displayname is not set fallback to user id */ } +

        send dm

        +

        invite to…

        +
        + }
      • ; }) } From dcbb6f7014703ab599173a26eef5eb8c7e802f65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Schn=C3=BCll?= Date: Sun, 23 Oct 2022 21:31:51 +0200 Subject: [PATCH 007/714] cleaning up and documenting --- pages/explore/infoAction.js | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/pages/explore/infoAction.js b/pages/explore/infoAction.js index 0173953a..e2f834bd 100644 --- a/pages/explore/infoAction.js +++ b/pages/explore/infoAction.js @@ -14,6 +14,7 @@ const InfoSection = styled.div` `; + /* * @TODO: * - adding routes for user interaction more -> members -> username @@ -26,12 +27,15 @@ const InfoAction = ({ currentId, stateEvents, userInfos }) => { const matrix = auth.getAuthenticationProvider('matrix'); const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); + //States + const [stateEventInformation, setStateEventInformation] = useState({}); + + //Effects useEffect(() => { processStateEvents(stateEvents); }, [stateEvents]); - const [stateEventInformation, setStateEventInformation] = useState({}); - + //Functions async function processStateEvents(stateEvents) { // gets the stateevents of the room const metaEvent = _.find(stateEvents, { type: 'dev.medienhaus.meta' })?.content; const nameEvent = _.find(stateEvents, { type: 'm.room.name' })?.content; @@ -40,8 +44,6 @@ const InfoAction = ({ currentId, stateEvents, userInfos }) => { const memberEvent = _.filter(stateEvents, { type: 'm.room.member' }); const topicEvent = _.find(stateEvents, { type: 'm.room.topic' })?.content; - console.log(joinRulesEvent); - const members = _.compact( //filter out empty ones _.map(memberEvent, member => { if (member?.content?.membership === 'leave') return; //check if the latest event was an leave, so the user is not a member anymore at this point @@ -50,13 +52,14 @@ const InfoAction = ({ currentId, stateEvents, userInfos }) => { const institutions = _.uniq(_.map(members, member => member?.id.split(':')[1])); //show only the tld's of the homeservers. '_.uniq' filters out duplicates - const initial = { name: nameEvent?.name, + const initial = { //contains only extracted data from stateEvents which are mentioned in the matrix specs + name: nameEvent?.name, topic: topicEvent?.content, members: members, join: joinRulesEvent?.join_rule, - visibility: historyVisibilityEvent?.history_visibility + visibility: historyVisibilityEvent?.history_visibility, }; - const custom = { + const custom = { //contains only data extracted from custom stateEvents template: metaEvent?.template, type: metaEvent?.type, application: metaEvent?.application, @@ -64,8 +67,7 @@ const InfoAction = ({ currentId, stateEvents, userInfos }) => { }; const stateInformations = { initial: initial, custom: custom }; - - setStateEventInformation({ ...stateEventInformation, custom: stateInformations.custom, initial: stateInformations.initial }); + setStateEventInformation({ ...stateEventInformation, custom: stateInformations.custom, initial: stateInformations.initial }); // applying the structured data to the observable State } return ( @@ -104,7 +106,7 @@ const InfoAction = ({ currentId, stateEvents, userInfos }) => { switch (stateEventInformation?.initial?.visibility) { case 'world_readable': return <>🌐; - case 'shared': + case 'shared': return <>📖; case 'joined': return <>🔐; @@ -158,6 +160,7 @@ const InfoAction = ({ currentId, stateEvents, userInfos }) => {
      Referenced + …
    From 66cff9a0d38b67ed3db7d48d15e6347f2ed55938 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Schn=C3=BCll?= Date: Mon, 24 Oct 2022 02:13:53 +0200 Subject: [PATCH 008/714] adding kick function to moderate action --- pages/explore/actions.js | 2 +- pages/explore/infoAction.js | 4 +- pages/explore/settingsAction.js | 113 +++++++++++++++++++++++++++++--- 3 files changed, 106 insertions(+), 13 deletions(-) diff --git a/pages/explore/actions.js b/pages/explore/actions.js index c66aee64..60cf2a87 100644 --- a/pages/explore/actions.js +++ b/pages/explore/actions.js @@ -75,7 +75,7 @@ const Actions = ({ currentId }) => { { showActions.infos && } { showActions.add && } - { showActions.settings && } + { showActions.settings && } diff --git a/pages/explore/infoAction.js b/pages/explore/infoAction.js index e2f834bd..46a212ca 100644 --- a/pages/explore/infoAction.js +++ b/pages/explore/infoAction.js @@ -36,7 +36,7 @@ const InfoAction = ({ currentId, stateEvents, userInfos }) => { }, [stateEvents]); //Functions - async function processStateEvents(stateEvents) { // gets the stateevents of the room + function processStateEvents(stateEvents) { // gets the stateevents of the room const metaEvent = _.find(stateEvents, { type: 'dev.medienhaus.meta' })?.content; const nameEvent = _.find(stateEvents, { type: 'm.room.name' })?.content; const joinRulesEvent = _.find(stateEvents, { type: 'm.room.join_rules' })?.content; @@ -146,7 +146,7 @@ const InfoAction = ({ currentId, stateEvents, userInfos }) => {
      { _.map(stateEventInformation?.initial?.members, (member, key) => { return
    • - { member?.id === userInfos?.id? <>{ member?.displaname ? member?.displaname : member?.id.split(':')[0].substring(1) } : //checks if the user is the logged in user, to disable interaction + { member?.id === userInfos?.id? <>{ member?.displaname ? member?.displaname : member?.id.split(':')[0].substring(1) } (you) : //checks if the user is the logged in user, to disable interaction
      { member?.displaname ? member?.displaname : member?.id.split(':')[0].substring(1) } { /* If Displayname is not set fallback to user id */ }

      send dm

      diff --git a/pages/explore/settingsAction.js b/pages/explore/settingsAction.js index aad1baf6..23602e8a 100644 --- a/pages/explore/settingsAction.js +++ b/pages/explore/settingsAction.js @@ -7,18 +7,39 @@ import { useAuth } from '../../lib/Auth'; import { useMatrix } from '../../lib/Matrix'; const ModifySection = styled.details` - & { - margin-bottom: var(--margin); - } + & { + margin-bottom: var(--margin); + } `; const CreateSubstructureSection = styled.details` - & { - margin-bottom: var(--margin); - } + & { + margin-bottom: var(--margin); + } +`; + +const MemberSection = styled.ul` + & { + margin-bottom: var(--margin); + } + + & button { + width: 50px; + } +`; + +const KickDialog = styled.div` + & { + margin-bottom: var(--margin); + } `; -const SettingsAction = ({ currentId }) => { +/* +* @TODO: +* - connecting stateEvents with Sync, so if a member got kicked the State will chance automatically +*/ + +const SettingsAction = ({ currentId, stateEvents, userInfos }) => { const auth = useAuth(); const matrix = auth.getAuthenticationProvider('matrix'); const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); @@ -81,15 +102,87 @@ const SettingsAction = ({ currentId }) => { } }; + const [stateEventInformation, setStateEventInformation] = useState({}); + + useEffect(() => { + populateInterface(stateEvents); + }, [stateEvents]); + + function populateInterface() { + const nameEvent = _.find(stateEvents, { type: 'm.room.name' })?.content; + const topicEvent = _.find(stateEvents, { type: 'm.room.topic' })?.content; + const memberEvent = _.filter(stateEvents, { type: 'm.room.member' }); + + const members = _.compact( //filter out empty ones + _.map(memberEvent, member => { + if (member?.content?.membership === 'leave') return; //check if the latest event was an leave, so the user is not a member anymore at this point + return { id: member?.sender, displaname: member?.content?.displayname }; + })); + + const initial = { //contains only extracted data from stateEvents which are mentioned in the matrix specs + name: nameEvent?.name, + topic: topicEvent?.content, + members: members, + }; + + const stateInformations = { initial: initial }; + setStateEventInformation({ ...stateEventInformation, custom: stateInformations.custom, initial: stateInformations.initial }); // applying the structured data to the observable State + } + + const onSave = async (e) => { + console.log(e?.target?.name + ':' + e?.target?.value); + if (e?.target?.name === 'roomName' && e?.target?.value ==! e?.target?.value) { + console.log('bing'); + } else { + console.log('bong'); + } + }; + + const [memberKickCandidate, setMemberKickCandidate] = useState(); + + const kickUser = async (userId, verfify) => { + if (verfify && memberKickCandidate) { + await matrixClient.kick(currentId, userId).catch(e => {console.log(e);}); + console.log(userId + ' kicked!'); + setMemberKickCandidate(''); + } + + setMemberKickCandidate(userId); + }; + return ( <> Modify - - - + + +
      members + + { _.map(stateEventInformation?.initial?.members, (member, key) => { + return
    • + { member?.id === userInfos?.id? <>{ member?.displaname ? member?.displaname : member?.id.split(':')[0].substring(1) } (you) : + <> +
      + { member?.displaname ? member?.displaname : member?.id.split(':')[0].substring(1) } { /* If Displayname is not set fallback to user id */ } +

      send dm

      +

      invite to…

      +
      + + { memberKickCandidate !== member?.id ? : + <> + + + + } + + + } +
    • ; + }) + } +
      advanced From 405f443a5fc8b49539e84ec1dd002d32984015d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Schn=C3=BCll?= Date: Tue, 25 Oct 2022 17:37:05 +0200 Subject: [PATCH 009/714] rewrite infoAction --- pages/explore/actions.js | 126 ++++++++++++++++++++++++++++++++---- pages/explore/infoAction.js | 100 ++++++++++------------------ 2 files changed, 150 insertions(+), 76 deletions(-) diff --git a/pages/explore/actions.js b/pages/explore/actions.js index 60cf2a87..ba08c844 100644 --- a/pages/explore/actions.js +++ b/pages/explore/actions.js @@ -48,24 +48,101 @@ const Actions = ({ currentId }) => { const [stateEvents, setStateEvents] = useState(); - const [userInfos, setUserInfos] = useState({mod:false}); //stores information about the curren User + const [userInfos, setUserInfos] = useState({ mod: false }); //stores information about the curren User useEffect(() => { - getStateEvents(currentId); + fetchCoreInformations(); }, [currentId]); - async function getStateEvents(roomId) { // gets the stateevents of the room - const events = await matrixClient.roomState(roomId).catch((e) => { }); - setStateEvents(events); + + /* + * ROOM DATA STORAGE + * ------------------ + * states which holds all nessesarry informations about the room as single source of truth for all subcomponents. + * this can be populated seperatatly to by user interactions and will not preload all informations at the beginnning + * this approach was chosen instead of just getting all stateEvents of the room for performance reason as it contains a stateEvent for each user, + * which will increase the requested pauload with a couple of thousends a lot. + * sadly as react evolves into this weird directions, the declaration of the variables, needs to be implemented like this, as been done by a 5 year old. + */ + + const [roomName, setRoomName] = useState(); + const [roomTopic, setRoomTopic] = useState(); + const [roomJoin, setRoomJoin] = useState(); + const [roomPowerLevels, setRoomPowerLevels] = useState(); + const [roomMembers, setRoomMembers] = useState({ list: [], institutions: [] }); + const [roomHistoryVisibility, setRoomHistoryVisibility] = useState(); + const [roomMeta, setMetaRoom] = useState({ type: '', template: '', application: [] }); + + // fetch functions to get room specific informations from the current Id + const fetchBasic = async () => { + const topicEvent = await matrixClient.getStateEvent( + currentId, + 'm.room.topic', + ).catch(() => {}); + const nameEvent = await matrixClient.getStateEvent( + currentId, + 'm.room.name', + ).catch(() => {}); + setRoomName(nameEvent?.name); + setRoomTopic(topicEvent?.topic); + }; + const fetchMembers = async () => { + const joinedMembers = await matrixClient.getJoinedRoomMembers(currentId).catch(() => {}); + const members = { }; + members.list = _.map(joinedMembers?.joined, (member, key) => { + return { + id: key, + displayname: member?.display_name, + }; + }); + members.institutions = _.uniq(_.map(members.list, member => member?.id.split(':')[1])); + console.log(members); + setRoomMembers(members); + }; + const fetchJoinRule = async () => { + const joinRulesEvent = await matrixClient.getStateEvent( + currentId, + 'm.room.join_rules', + ).catch(() => {}); + setRoomJoin(joinRulesEvent?.join_rule); + }; + const fetchHistoryVisibility = async () => { + const historyVisibilityEvent = await matrixClient.getStateEvent( + currentId, + 'm.room.history_visibility', + ).catch(() => {}); + setRoomHistoryVisibility(historyVisibilityEvent?.history_visibility); + }; + const fetchPowerLevels = async () => { const userId = matrixClient?.credentials?.userId; - const powerLevelsEvent = _.find(events, { type: 'm.room.power_levels' })?.content; + const powerLevelsEvent = await matrixClient.getStateEvent( + currentId, + 'm.room.power_levels', + ).catch(() => {}); const modRights = powerLevelsEvent?.users[userId] >= 50; //check if the current user got is listed with a custom power level if true and >= 50 (mod default) mod flag is set true setUserInfos({ ...userInfos, id: userId, mod: modRights }); - } + setRoomPowerLevels(powerLevelsEvent?.content); + }; + + // Custom State Events + const fetchMeta = async () => { + const metaEvent = await matrixClient.getStateEvent( + currentId, + 'dev.medienhaus.meta', + ).catch(() => {}); + setMetaRoom(metaEvent); + }; + const fetchCoreInformations = async () => { + await fetchBasic(); + await fetchJoinRule(); + await fetchHistoryVisibility(); + await fetchPowerLevels(); + }; return ( - getStateEvents(currentId)}>… + + @@ -73,9 +150,36 @@ const Actions = ({ currentId }) => { - { showActions.infos && } - { showActions.add && } - { showActions.settings && } + { showActions.infos && + } + { showActions.add && } + { showActions.settings && + } diff --git a/pages/explore/infoAction.js b/pages/explore/infoAction.js index 46a212ca..845308e4 100644 --- a/pages/explore/infoAction.js +++ b/pages/explore/infoAction.js @@ -22,71 +22,35 @@ const InfoSection = styled.div` * - searching for referenced in root tree (slow) */ -const InfoAction = ({ currentId, stateEvents, userInfos }) => { - const auth = useAuth(); - const matrix = auth.getAuthenticationProvider('matrix'); - const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); - - //States - const [stateEventInformation, setStateEventInformation] = useState({}); - - //Effects - useEffect(() => { - processStateEvents(stateEvents); - }, [stateEvents]); - - //Functions - function processStateEvents(stateEvents) { // gets the stateevents of the room - const metaEvent = _.find(stateEvents, { type: 'dev.medienhaus.meta' })?.content; - const nameEvent = _.find(stateEvents, { type: 'm.room.name' })?.content; - const joinRulesEvent = _.find(stateEvents, { type: 'm.room.join_rules' })?.content; - const historyVisibilityEvent = _.find(stateEvents, { type: 'm.room.history_visibility' })?.content; - const memberEvent = _.filter(stateEvents, { type: 'm.room.member' }); - const topicEvent = _.find(stateEvents, { type: 'm.room.topic' })?.content; - - const members = _.compact( //filter out empty ones - _.map(memberEvent, member => { - if (member?.content?.membership === 'leave') return; //check if the latest event was an leave, so the user is not a member anymore at this point - return { id: member?.sender, displaname: member?.content?.displayname }; - })); - - const institutions = _.uniq(_.map(members, member => member?.id.split(':')[1])); //show only the tld's of the homeservers. '_.uniq' filters out duplicates - - const initial = { //contains only extracted data from stateEvents which are mentioned in the matrix specs - name: nameEvent?.name, - topic: topicEvent?.content, - members: members, - join: joinRulesEvent?.join_rule, - visibility: historyVisibilityEvent?.history_visibility, - }; - const custom = { //contains only data extracted from custom stateEvents - template: metaEvent?.template, - type: metaEvent?.type, - application: metaEvent?.application, - institutions: institutions, - }; - - const stateInformations = { initial: initial, custom: custom }; - setStateEventInformation({ ...stateEventInformation, custom: stateInformations.custom, initial: stateInformations.initial }); // applying the structured data to the observable State - } - +const InfoAction = ({ + currentId, + userInfos, + members, + name, + topic, + join, + historyVisibility, + meta, + getMembers, + getMeta, +}) => { return (
      - { stateEventInformation?.initial?.name ? <> + { name ? <>
      Name
      -
      { stateEventInformation?.initial?.name }
      +
      { name }
      : <> } - { stateEventInformation?.initial?.topic ? <> + { topic ? <>
      Topic
      -
      { stateEventInformation?.initial?.topic }
      +
      { topic }
      : <> } <>
      Join Rules
      { (() => { - switch (stateEventInformation?.initial?.join) { + switch (join) { case 'public': return <>🌐; case 'restricted': // is the case if is member is also member of a different specified room (aka spacemember function in element) @@ -103,7 +67,7 @@ const InfoAction = ({ currentId, stateEvents, userInfos }) => { <>
      Visibility
      { (() => { - switch (stateEventInformation?.initial?.visibility) { + switch (historyVisibility) { case 'world_readable': return <>🌐; case 'shared': @@ -121,36 +85,42 @@ const InfoAction = ({ currentId, stateEvents, userInfos }) => {
      more

      Id: { currentId }

      -
      +
      meta
      -
      Application
      -
      { stateEventInformation?.custom?.type }
      + { meta?.application ? + <> +
      Application
      +
      { meta?.application }
      + + : <> + }
      Type
      -
      { stateEventInformation?.custom?.type }
      +
      { meta?.type }
      Template
      -
      { stateEventInformation?.custom?.template }
      +
      { meta?.template }
      -
      +
      0 ? undefined : getMembers}> Institutions
        - { _.map(stateEventInformation?.custom?.institutions, (institution, key) => { + { _.map(members?.institutions, (institution, key) => { return
      • { institution }
      • ; }) }
      -
      +
      0 ? undefined : getMembers}> Members
        - { _.map(stateEventInformation?.initial?.members, (member, key) => { + { _.map(members?.list, (member, key) => { return
      • - { member?.id === userInfos?.id? <>{ member?.displaname ? member?.displaname : member?.id.split(':')[0].substring(1) } (you) : //checks if the user is the logged in user, to disable interaction + { member?.id === userInfos?.id? <>{ member?.displayname ? member?.displayname : member?.id.split(':')[0].substring(1) } (you) : //checks if the user is the logged in user, to disable interaction
        - { member?.displaname ? member?.displaname : member?.id.split(':')[0].substring(1) } { /* If Displayname is not set fallback to user id */ } + { member?.displayname ? member?.displayname : member?.id.split(':')[0].substring(1) } { /* If Displayname is not set fallback to user id */ }

        send dm

        invite to…

        +

        contextualize…

        }
      • ; From 04976623ece0546338502fabc850e40b89b9fb56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Schn=C3=BCll?= Date: Wed, 26 Oct 2022 01:02:09 +0200 Subject: [PATCH 010/714] adding remove action placeholder --- pages/explore/actions.js | 92 +++++++++++++++++++++++------------ pages/explore/removeAction.js | 45 +++++++++++++++++ 2 files changed, 105 insertions(+), 32 deletions(-) create mode 100644 pages/explore/removeAction.js diff --git a/pages/explore/actions.js b/pages/explore/actions.js index ba08c844..fe5cb111 100644 --- a/pages/explore/actions.js +++ b/pages/explore/actions.js @@ -8,11 +8,21 @@ import { useMatrix } from '../../lib/Matrix'; import SettingsAction from './settingsAction'; import InfoAction from './infoAction'; import AddAction from './addAction'; +import RemoveAction from './removeAction'; const ActionsSection = styled.details` & { margin-bottom: var(--margin); - + + } + & > summary { + background-color: var(--color-fg); + color: var(--color-bg); + width: 50px; + height: 35px; + list-style: none; + text-align: center; + font-weight:bold; } & > div > button { @@ -54,39 +64,41 @@ const Actions = ({ currentId }) => { fetchCoreInformations(); }, [currentId]); - - /* + /** * ROOM DATA STORAGE * ------------------ * states which holds all nessesarry informations about the room as single source of truth for all subcomponents. * this can be populated seperatatly to by user interactions and will not preload all informations at the beginnning * this approach was chosen instead of just getting all stateEvents of the room for performance reason as it contains a stateEvent for each user, * which will increase the requested pauload with a couple of thousends a lot. - * sadly as react evolves into this weird directions, the declaration of the variables, needs to be implemented like this, as been done by a 5 year old. + * sadly as react evolves into this weird directions, the declaration of the variables needed to be implemented like this; + * in an non nested way, feels like written by a 5 year old. */ const [roomName, setRoomName] = useState(); const [roomTopic, setRoomTopic] = useState(); - const [roomJoin, setRoomJoin] = useState(); + const [roomJoinRule, setRoomJoinRule] = useState(); const [roomPowerLevels, setRoomPowerLevels] = useState(); const [roomMembers, setRoomMembers] = useState({ list: [], institutions: [] }); const [roomHistoryVisibility, setRoomHistoryVisibility] = useState(); const [roomMeta, setMetaRoom] = useState({ type: '', template: '', application: [] }); // fetch functions to get room specific informations from the current Id - const fetchBasic = async () => { - const topicEvent = await matrixClient.getStateEvent( - currentId, - 'm.room.topic', - ).catch(() => {}); + const fetchRoomName = async () => { const nameEvent = await matrixClient.getStateEvent( currentId, 'm.room.name', ).catch(() => {}); setRoomName(nameEvent?.name); + }; + const fetchRoomTopic = async () => { + const topicEvent = await matrixClient.getStateEvent( + currentId, + 'm.room.topic', + ).catch(() => {}); setRoomTopic(topicEvent?.topic); }; - const fetchMembers = async () => { + const fetchRoomMembers = async () => { const joinedMembers = await matrixClient.getJoinedRoomMembers(currentId).catch(() => {}); const members = { }; members.list = _.map(joinedMembers?.joined, (member, key) => { @@ -96,24 +108,23 @@ const Actions = ({ currentId }) => { }; }); members.institutions = _.uniq(_.map(members.list, member => member?.id.split(':')[1])); - console.log(members); setRoomMembers(members); }; - const fetchJoinRule = async () => { + const fetchRoomJoinRule = async () => { const joinRulesEvent = await matrixClient.getStateEvent( currentId, 'm.room.join_rules', ).catch(() => {}); - setRoomJoin(joinRulesEvent?.join_rule); + setRoomJoinRule(joinRulesEvent?.join_rule); }; - const fetchHistoryVisibility = async () => { + const fetchRoomHistoryVisibility = async () => { const historyVisibilityEvent = await matrixClient.getStateEvent( currentId, 'm.room.history_visibility', ).catch(() => {}); setRoomHistoryVisibility(historyVisibilityEvent?.history_visibility); }; - const fetchPowerLevels = async () => { + const fetchRoomPowerLevels = async () => { const userId = matrixClient?.credentials?.userId; const powerLevelsEvent = await matrixClient.getStateEvent( currentId, @@ -125,7 +136,7 @@ const Actions = ({ currentId }) => { }; // Custom State Events - const fetchMeta = async () => { + const fetchRoomMeta = async () => { const metaEvent = await matrixClient.getStateEvent( currentId, 'dev.medienhaus.meta', @@ -133,20 +144,22 @@ const Actions = ({ currentId }) => { setMetaRoom(metaEvent); }; const fetchCoreInformations = async () => { - await fetchBasic(); - await fetchJoinRule(); - await fetchHistoryVisibility(); - await fetchPowerLevels(); + await fetchRoomName(); + await fetchRoomTopic(); + await fetchRoomJoinRule(); + await fetchRoomHistoryVisibility(); + await fetchRoomPowerLevels(); }; return ( - - - - + + + + + @@ -157,28 +170,43 @@ const Actions = ({ currentId }) => { members={roomMembers} name={roomName} topic={roomTopic} - join={roomJoin} + join={roomJoinRule} historyVisibility={roomHistoryVisibility} meta={roomMeta} - getMembers={fetchMembers} - getMeta={fetchMeta} + getMembers={fetchRoomMembers} + getMeta={fetchRoomMeta} /> } { showActions.add && } + { showActions.remove && } { showActions.settings && } diff --git a/pages/explore/removeAction.js b/pages/explore/removeAction.js new file mode 100644 index 00000000..3d72c25e --- /dev/null +++ b/pages/explore/removeAction.js @@ -0,0 +1,45 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import styled from 'styled-components'; +import getConfig from 'next/config'; +import _ from 'lodash'; + +import { useAuth } from '../../lib/Auth'; +import { useMatrix } from '../../lib/Matrix'; + + +import CreateContext from './createContext'; +import TemplateSelect from './templateSelect'; + +const ModSection = styled.div` + & { + margin-bottom: var(--margin); + } + & > button { + margin-bottom: var(--margin); + } +`; + +const UserSection = styled.div` + & { + margin-bottom: var(--margin); + } + + & > button { + margin-bottom: var(--margin); + } +`; + +const RemoveAction = ({ currentId, parentId, userInfos }) => { + const auth = useAuth(); + const matrix = auth.getAuthenticationProvider('matrix'); + const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); + + return ( + <> +

        hello :)

        + + + ); +}; + +export default RemoveAction; From e5119a99c669e0e5d505cbeacceeb4b842dc04cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Schn=C3=BCll?= Date: Wed, 26 Oct 2022 01:02:39 +0200 Subject: [PATCH 011/714] outsourcing the presets to an own file --- pages/explore/settingPresets.js | 119 ++++++++++++++ pages/explore/settingsAction.js | 266 +++++++++++++------------------- 2 files changed, 225 insertions(+), 160 deletions(-) create mode 100644 pages/explore/settingPresets.js diff --git a/pages/explore/settingPresets.js b/pages/explore/settingPresets.js new file mode 100644 index 00000000..31f0827f --- /dev/null +++ b/pages/explore/settingPresets.js @@ -0,0 +1,119 @@ +// eslint-disable-next-line no-undef +module.exports = { + name: { + allowedCharacter: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-', + min: 1, + max: 100, + }, + topic: { + allowedCharacter: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-', + min: 1, + max: 100, + }, + allowedJoinRules: [ + { + name: 'public', + display: '🌐', + description: 'public to everyone to join', + }, + { + name: 'restricted', + display: '🔐', + description: 'member is also member of a different specified room (aka spacemember function in element)', + }, + { + name: 'knock', + display: '🚪', + description: 'people can request to join, you have the chance to accept or reject those requests. after acceptens the requested user will get an invite.', + }, + { + name: 'invite', + display: '🔒', + description: 'only people you invite can join', + }, + ], + allowedHistoryVisibility: [ + { + name: 'world_readable', + display: '🌐', + description: 'some description', + }, + { + name: 'shared', + display: '📖', + description: 'some description', + }, + { + name: 'joined', + display: '🔐', + description: 'some description', + }, + { + name: 'invited', + display: '🔒', + description: 'some description', + }, + ], + allowedPowerLevelPresets: [ + { + name: 'public', + display: '🌐', + description: 'public (default); all members can add content.', + powerLevels: { + 'users_default': 0, + 'events': { + 'm.room.avatar': 50, + 'm.room.canonical_alias': 50, + 'm.room.encryption': 100, + 'm.room.history_visibility': 100, + 'm.room.name': 50, + 'm.room.power_levels': 50, + 'm.room.server_acl': 100, + 'm.room.tombstone': 100, + 'm.space.child': 0, + 'm.room.topic': 50, + 'm.room.pinned_events': 50, + 'm.reaction': 50, + 'dev.medienhaus.meta': 100, + }, + 'events_default': 50, + 'state_default': 50, + 'ban': 50, + 'kick': 50, + 'redact': 50, + 'invite': 50, + 'historical': 100, + }, + }, + { + name: 'announce', + display: '📣', + description: 'announce; read only. only moderators and admins can add things.', + powerLevels: { + 'users_default': 0, + 'events': { + 'm.room.avatar': 50, + 'm.room.canonical_alias': 50, + 'm.room.encryption': 100, + 'm.room.history_visibility': 100, + 'm.room.name': 50, + 'm.room.power_levels': 50, + 'm.room.server_acl': 100, + 'm.room.tombstone': 100, + 'm.space.child': 0, + 'm.room.topic': 50, + 'm.room.pinned_events': 50, + 'm.reaction': 50, + 'dev.medienhaus.meta': 100, + }, + 'events_default': 50, + 'state_default': 50, + 'ban': 50, + 'kick': 50, + 'redact': 50, + 'invite': 50, + 'historical': 100, + }, + }, + ], +}; diff --git a/pages/explore/settingsAction.js b/pages/explore/settingsAction.js index 23602e8a..ba8accc2 100644 --- a/pages/explore/settingsAction.js +++ b/pages/explore/settingsAction.js @@ -1,19 +1,19 @@ -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import styled from 'styled-components'; -import getConfig from 'next/config'; import _ from 'lodash'; +import TemplateSelect from './templateSelect'; +import UserHandle from './userHandle'; import { useAuth } from '../../lib/Auth'; import { useMatrix } from '../../lib/Matrix'; +import settingsPresets from './settingPresets'; -const ModifySection = styled.details` +const SettingsSection = styled.div` & { margin-bottom: var(--margin); } -`; -const CreateSubstructureSection = styled.details` - & { + & input, select { margin-bottom: var(--margin); } `; @@ -34,108 +34,72 @@ const KickDialog = styled.div` } `; -/* -* @TODO: -* - connecting stateEvents with Sync, so if a member got kicked the State will chance automatically +/** + * @TODO: + * - connecting stateEvents with Sync, so if a member got kicked the State will chance automatically without an additional call. +*/ + +/** + * COMPONENT 'SettingsAction' + * Not all parameters will be explained in detail as the functionallity is quite redundant based on a simmilar naming scheme + * @param {String} currentId — the Id of the current observed Room + * @param {Object} userInfos — contains the informations about the current loggedIn User + * @param {String} name – x + * @param {function} setName – x + * @param {async function} refreshName – x + * … + * setMembers is not given as the interaction with the matrix server is all been done in this compontent itself for the members */ -const SettingsAction = ({ currentId, stateEvents, userInfos }) => { +const SettingsAction = ({ + currentId, + onInfoChange, + userInfos, + name, + setName, + refreshName, + topic, + setTopic, + refreshTopic, + join, + setJoin, + refreshJoin, + historyVisibility, + sethistoryVisibility, + refreshHistoryVisibility, + powerLevels, + setPowerLevels, + refreshPowerLevels, + members, + refreshMembers, +}) => { const auth = useAuth(); - const matrix = auth.getAuthenticationProvider('matrix'); const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); - // Logics for the functions of the 'CreateSubstructureSection' - // includes some basic input validation and dynamic form adjustments for the template selection - - const [contextTemplates, setContextTemplates] = useState([]); - - const [generateNewTemplate, setGenerateNewTemplate] = useState(false); - - const [createNew, setCreateNew] = useState({ name: '', template: '', parent: '', interfaceError: '' }); //stores all nessesarry user input from the form - - async function getTemplatesOfContexts(roomId) { // gets the templates from the - if (contextTemplates.length > 0) return; - const metaEvent = await auth.getAuthenticationProvider('matrix').getMatrixClient().getStateEvent(roomId, 'dev.medienhaus.meta').catch(() => {}); - if ((metaEvent?.template !== 'templates')) return; - console.log(metaEvent); - const roomContent = await (matrix.fetchRoomMessages(roomId, 5)); - const templates = _.uniq(roomContent?.chunk.map(entry => entry?.content?.body)).filter(e => e); - setContextTemplates(templates); - } - - useEffect(() => { // basic input validation for the input fields to create a new substructure - if (createNew.name === '' || createNew?.name.length < 4) { - createNew.interfaceError = 'name too short'; - return; - } else { - createNew.interfaceError = ''; - } - - if (createNew.template === '' || createNew?.template.length < 4) { - createNew.interfaceError = 'template name too short'; - return; - } else { - createNew.interfaceError = ''; - } - }, [createNew]); - - const createContext = (e) => { - e.preventDefault(); - if (currentId.length > 10 && currentId.charAt(0) === '!' && currentId.includes(':')) { //just some really really basic check if it could be an matrix id - setCreateNew({ ...createNew, parent: currentId }); - console.log('created'); - } else { - createNew.interfaceError = 'something went wrong with the selected matrix Id, please reload'; - } - }; - - const createNewChangeHandle = (e) => { - if (e.target.name === 'template' && e.target.value === '_createNew') {//check if dropdown is selected for new to modify form - e.target.value === '_createNew'? setGenerateNewTemplate(true) : setGenerateNewTemplate(false); - setCreateNew({ ...createNew, template: '' }); - return; - } - if (e.target.name === 'newTemplate') { - setCreateNew({ ...createNew, template: e.target.value }); - } else { - setCreateNew({ ...createNew, [e.target.name]: e.target.value }); //this is the regular way if no errors occured before - } + /** + * OnSave Functions + * For all OnSaveCalled to the matrix server it doesn't matter if the content has not changed and the call is still executed + * as matrix will return the same event_id without creating a new one if the content + * of the Call is identical with the last event_id content. + * So therefore no additional client side checking is absolutly nessesarry if the content has changed at all. + */ + + const onTopicSave = async () => { + if (topic?.length < 1) return; + const contentToSend = ''+topic; + const call = await matrixClient.setRoomTopic(currentId, contentToSend); + console.log(call); + if (!call?.event_id) return;//show error and return + await refreshName(); //refresh with the data from the server side to double ckeck as a verfication if not yet another user changed it in the meantime + if (contentToSend !== topic) return; //show error that somethign changed in the meantime }; - const [stateEventInformation, setStateEventInformation] = useState({}); - - useEffect(() => { - populateInterface(stateEvents); - }, [stateEvents]); - - function populateInterface() { - const nameEvent = _.find(stateEvents, { type: 'm.room.name' })?.content; - const topicEvent = _.find(stateEvents, { type: 'm.room.topic' })?.content; - const memberEvent = _.filter(stateEvents, { type: 'm.room.member' }); - - const members = _.compact( //filter out empty ones - _.map(memberEvent, member => { - if (member?.content?.membership === 'leave') return; //check if the latest event was an leave, so the user is not a member anymore at this point - return { id: member?.sender, displaname: member?.content?.displayname }; - })); - - const initial = { //contains only extracted data from stateEvents which are mentioned in the matrix specs - name: nameEvent?.name, - topic: topicEvent?.content, - members: members, - }; - - const stateInformations = { initial: initial }; - setStateEventInformation({ ...stateEventInformation, custom: stateInformations.custom, initial: stateInformations.initial }); // applying the structured data to the observable State - } - - const onSave = async (e) => { - console.log(e?.target?.name + ':' + e?.target?.value); - if (e?.target?.name === 'roomName' && e?.target?.value ==! e?.target?.value) { - console.log('bing'); - } else { - console.log('bong'); - } + const onNameSave = async () => { + if (topic?.length < 1) return; + const call = await matrixClient.setRoomTopic(currentId, topic); + console.log(call); + if (!call?.event_id) return;//show error and return + await refreshName(); }; const [memberKickCandidate, setMemberKickCandidate] = useState(); @@ -145,6 +109,7 @@ const SettingsAction = ({ currentId, stateEvents, userInfos }) => { await matrixClient.kick(currentId, userId).catch(e => {console.log(e);}); console.log(userId + ' kicked!'); setMemberKickCandidate(''); + //onInfoChange() } setMemberKickCandidate(userId); @@ -152,25 +117,51 @@ const SettingsAction = ({ currentId, stateEvents, userInfos }) => { return ( <> - - Modify - - - + + + {setName(e.target.value);}} name="name" value={name} onBlur={onNameSave} /> + {setTopic(e.target.value);}} name="topic" value={topic} onBlur={onTopicSave} />
        + advanced + + + +
        +
        0 ? undefined : refreshMembers}> members - { _.map(stateEventInformation?.initial?.members, (member, key) => { + { _.map(members?.list, (member, key) => { return
      • - { member?.id === userInfos?.id? <>{ member?.displaname ? member?.displaname : member?.id.split(':')[0].substring(1) } (you) : + { member?.id === userInfos?.id ? + <> + { + member?.displayname ? + member?.displayname : + member?.id.split(':')[0].substring(1) + } (you) + + : <> -
        - { member?.displaname ? member?.displaname : member?.id.split(':')[0].substring(1) } { /* If Displayname is not set fallback to user id */ } -

        send dm

        -

        invite to…

        -
        + - { memberKickCandidate !== member?.id ? : + { memberKickCandidate !== member?.id ? + + : <> @@ -184,57 +175,12 @@ const SettingsAction = ({ currentId, stateEvents, userInfos }) => { }
      • -
        - advanced - - - -
        danger zone
        -
        - getTemplatesOfContexts(getConfig().publicRuntimeConfig.templates.context)}> { /* will only load the templates after expanding this view to prevent unnecessary network traffic */ } - create Substructure -
        - - - { generateNewTemplate && }{ /* this input is only generated if the dropwdown was selected 'create new…' */ } - -
        - advanced - - - -
        - - { createNew.interfaceError &&

        ‼️ { createNew.interfaceError }

        } { /* Showing the current Error to the user if some input validation failed */ } - -
        -
        + From fbae761664bb531423a6c45e139a9ce9373de433 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Schn=C3=BCll?= Date: Wed, 26 Oct 2022 01:03:17 +0200 Subject: [PATCH 012/714] outsourcing template select and user handeling --- pages/explore/addAction.js | 4 ++ pages/explore/createContext.js | 74 +++++++++++++++++++++++++++++++++ pages/explore/infoAction.js | 17 ++++---- pages/explore/templateSelect.js | 63 ++++++++++++++++++++++++++++ pages/explore/userHandle.js | 27 ++++++++++++ 5 files changed, 178 insertions(+), 7 deletions(-) create mode 100644 pages/explore/createContext.js create mode 100644 pages/explore/templateSelect.js create mode 100644 pages/explore/userHandle.js diff --git a/pages/explore/addAction.js b/pages/explore/addAction.js index 530372f8..5ce6f5c1 100644 --- a/pages/explore/addAction.js +++ b/pages/explore/addAction.js @@ -6,6 +6,10 @@ import _ from 'lodash'; import { useAuth } from '../../lib/Auth'; import { useMatrix } from '../../lib/Matrix'; + +import CreateContext from './createContext'; +import TemplateSelect from './templateSelect'; + const ModSection = styled.div` & { margin-bottom: var(--margin); diff --git a/pages/explore/createContext.js b/pages/explore/createContext.js new file mode 100644 index 00000000..ff0e98bd --- /dev/null +++ b/pages/explore/createContext.js @@ -0,0 +1,74 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import styled from 'styled-components'; +import getConfig from 'next/config'; +import _ from 'lodash'; + +import { useAuth } from '../../lib/Auth'; +import { useMatrix } from '../../lib/Matrix'; + +const CreateSubstructureSection = styled.details` + & { + margin-bottom: var(--margin); + } +`; + +const CreateContext = ({ currentId }) => { + const auth = useAuth(); + const matrix = auth.getAuthenticationProvider('matrix'); + const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); + + + useEffect(() => { // basic input validation for the input fields to create a new substructure + if (createNew.name === '' || createNew?.name.length < 4) { + createNew.interfaceError = 'name too short'; + return; + } else { + createNew.interfaceError = ''; + } + + if (createNew.template === '' || createNew?.template.length < 4) { + createNew.interfaceError = 'template name too short'; + return; + } else { + createNew.interfaceError = ''; + } + }, [createNew]); + + const createContext = (e) => { + e.preventDefault(); + if (currentId.length > 10 && currentId.charAt(0) === '!' && currentId.includes(':')) { //just some really really basic check if it could be an matrix id + setCreateNew({ ...createNew, parent: currentId }); + console.log('created'); + } else { + createNew.interfaceError = 'something went wrong with the selected matrix Id, please reload'; + } + }; + + return ( + getTemplatesOfContexts(getConfig().publicRuntimeConfig.templates.context)}> { /* will only load the templates after expanding this view to prevent unnecessary network traffic */ } + create Substructure +
        + + +
        + advanced + + + +
        + + { createNew.interfaceError &&

        ‼️ { createNew.interfaceError }

        } { /* Showing the current Error to the user if some input validation failed */ } + +
        +
        + + ); +}; + +export default CreateContext; diff --git a/pages/explore/infoAction.js b/pages/explore/infoAction.js index 845308e4..89779f49 100644 --- a/pages/explore/infoAction.js +++ b/pages/explore/infoAction.js @@ -5,6 +5,7 @@ import _ from 'lodash'; import { useAuth } from '../../lib/Auth'; import { useMatrix } from '../../lib/Matrix'; +import UserHandle from './userHandle'; const InfoSection = styled.div` & { @@ -115,13 +116,15 @@ const InfoAction = ({
          { _.map(members?.list, (member, key) => { return
        • - { member?.id === userInfos?.id? <>{ member?.displayname ? member?.displayname : member?.id.split(':')[0].substring(1) } (you) : //checks if the user is the logged in user, to disable interaction -
          - { member?.displayname ? member?.displayname : member?.id.split(':')[0].substring(1) } { /* If Displayname is not set fallback to user id */ } -

          send dm

          -

          invite to…

          -

          contextualize…

          -
          + { member?.id === userInfos?.id ? //checks if the user is the logged in user, to disable interaction + <> + { + member?.displayname ? + member?.displayname : + member?.id.split(':')[0].substring(1) + } (you) + : + }
        • ; }) diff --git a/pages/explore/templateSelect.js b/pages/explore/templateSelect.js new file mode 100644 index 00000000..4c1af312 --- /dev/null +++ b/pages/explore/templateSelect.js @@ -0,0 +1,63 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import styled from 'styled-components'; +import getConfig from 'next/config'; +import _ from 'lodash'; + +import { useAuth } from '../../lib/Auth'; +import { useMatrix } from '../../lib/Matrix'; + +const TemplateSelect = ({ currentId }) => { + const auth = useAuth(); + const matrix = auth.getAuthenticationProvider('matrix'); + const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); + + const [createNew, setCreateNew] = useState({ name: '', template: '', parent: '', interfaceError: '' }); //stores all nessesarry user input from the form + const [generateNewTemplate, setGenerateNewTemplate] = useState(false); + + // Logics for the functions of the 'CreateSubstructureSection' + // includes some basic input validation and dynamic form adjustments for the template selection + + const [contextTemplates, setContextTemplates] = useState([]); + + async function getTemplatesOfContexts(roomId) { // gets the templates from the + if (contextTemplates.length > 0) return; + const metaEvent = await auth.getAuthenticationProvider('matrix').getMatrixClient().getStateEvent(roomId, 'dev.medienhaus.meta').catch(() => {}); + if ((metaEvent?.template !== 'templates')) return; + console.log(metaEvent); + const roomContent = await (matrix.fetchRoomMessages(roomId, 5)); + const templates = _.uniq(roomContent?.chunk.map(entry => entry?.content?.body)).filter(e => e); + setContextTemplates(templates); + } + + const createNewChangeHandle = (e) => { + if (e.target.name === 'template' && e.target.value === '_createNew') {//check if dropdown is selected for new to modify form + e.target.value === '_createNew'? setGenerateNewTemplate(true) : setGenerateNewTemplate(false); + setCreateNew({ ...createNew, template: '' }); + return; + } + if (e.target.name === 'newTemplate') { + setCreateNew({ ...createNew, template: e.target.value }); + } else { + setCreateNew({ ...createNew, [e.target.name]: e.target.value }); //this is the regular way if no errors occured before + } + }; + + return ( + <> + + { generateNewTemplate && }{ /* this input is only generated if the dropwdown was selected 'create new…' */ } + + + ); +}; + +export default TemplateSelect; diff --git a/pages/explore/userHandle.js b/pages/explore/userHandle.js new file mode 100644 index 00000000..d5457de9 --- /dev/null +++ b/pages/explore/userHandle.js @@ -0,0 +1,27 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import styled from 'styled-components'; + +const User = styled.div` + & { + margin-bottom: var(--margin); + } + & > button { + margin-bottom: var(--margin); + } +`; + +const UserHandle = ({ userId }) => { + + return ( + +
          + { userId?.displayname ? userId?.displayname : userId?.id.split(':')[0].substring(1) } { /* If Displayname is not set fallback to user id */ } +

          send dm

          +

          invite to…

          +

          contextualize…

          +
          +
          + ); +}; + +export default UserHandle; From cf4cc42a9883c9cf986a576074e8580b68ef3bb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Schn=C3=BCll?= Date: Wed, 26 Oct 2022 18:18:15 +0200 Subject: [PATCH 013/714] adding remove action --- pages/explore/actions.js | 155 ++++++++++++++++++++++++++------ pages/explore/index.js | 6 +- pages/explore/infoAction.js | 10 +-- pages/explore/removeAction.js | 72 ++++++++++++--- pages/explore/settingPresets.js | 1 + 5 files changed, 198 insertions(+), 46 deletions(-) diff --git a/pages/explore/actions.js b/pages/explore/actions.js index fe5cb111..e48de1c0 100644 --- a/pages/explore/actions.js +++ b/pages/explore/actions.js @@ -49,23 +49,79 @@ const MenuSection = styled.div` } `; -const Actions = ({ currentId }) => { +/** + * ACTIONS COMPONENT + * ------------------ + * + * @param {String} currentId — the Id of the current observed Room + * @param {String} parentId — the Id of the parent of the currently observed Room + * @param {function} popActiveContexts – deletes the latest Element of the Contexts Multi Level Select Stack. Needed for the remove action. + * +*/ + +const Actions = ({ currentId, parentId, popActiveContexts }) => { + /** + * MATRIX + * ------------------ + * @TODO + * - adding functionallity of updates of the sync call. to update cached states in this component which are changed in the meantime of this session by another user. + * this would needs to pipe all nessesary sync informations for the currently observed id into the current room data storage (volatile). + * in addition to keeping track also of some specific state events of previously observed id's into the cached roomdata, for example the powerlevel's. + */ + const auth = useAuth(); const matrix = auth.getAuthenticationProvider('matrix'); const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); - const [showActions, setShowActions] = useState({ modify: false, infos: false, add: false }); + /** + * GENERIC STATES + * ------------------ + * + */ - const [stateEvents, setStateEvents] = useState(); + const [showActions, setShowActions] = useState(); + const [userInfos, setUserInfos] = useState(); //stores information about the curren User - const [userInfos, setUserInfos] = useState({ mod: false }); //stores information about the curren User + const [userCurrentIdMod, setUserCurrentIdMod] = useState(false); + const [userParentIdMod, setParentIdMod] = useState(false); + + /** + * EFFECTS + * ------------------ + * observing if the currentId or the parentId changed. Both observed states are parameters of this action component given by the instantiating parent compontent. + * currentId: If the currentId changed some core informations about the currently observed Id will be collected. + * parentId: If the parentId changed the nessesary collected information of the Id are already been stored in a cache. + * This function will therefore just check if the user has the rights to remove the current observed Id from the parents to enables/disables this action in the render. + */ useEffect(() => { - fetchCoreInformations(); + fetchRoomName(); + fetchRoomTopic(); + fetchRoomJoinRule(); + fetchRoomHistoryVisibility(); + fetchRoomPowerLevels(); }, [currentId]); + useEffect(() => { + if (parentId) { + const parentPowerLevel = cachedRoomPowerLevels?.[parentId]?.powerLevels; + if (!parentPowerLevel) { + /** if a parentId exists but there is no cached record of the PowerLevel no parentMod rights are accesible to check and therefor set it to false. + * @TODO + * - could become a problem in the future if the ContextMulitLevelSelect Component will have the ability to start not from the root of the tree, so therefore the parent Events are not cached. + */ + setParentIdMod(false); + return; + } + setParentIdMod(parentPowerLevel?.users[userInfos?.id] >= 50); + } + }, [parentId]); + + useEffect(() => { + console.log(userInfos?.id); + }, [userParentIdMod]); /** - * ROOM DATA STORAGE + * CURRENT ROOM DATA STORAGE (VOLATILE) * ------------------ * states which holds all nessesarry informations about the room as single source of truth for all subcomponents. * this can be populated seperatatly to by user interactions and will not preload all informations at the beginnning @@ -83,13 +139,35 @@ const Actions = ({ currentId }) => { const [roomHistoryVisibility, setRoomHistoryVisibility] = useState(); const [roomMeta, setMetaRoom] = useState({ type: '', template: '', application: [] }); - // fetch functions to get room specific informations from the current Id + /** + * ALL ROOM DATA STORAGE (SESSION NON VOLATILE) + * ------------------ + * the following states keeps a cached copy of some specific state events which are already requested from the matrix server to use them later in an different usecase again to prevent the need for an additional call. + * The current usecase for which this is needed is to check the interactions with a parent id to remove spaces from an previously viewed id. + * + * @TODO + * - still needs to be discussed how to deal with edgecases like: an additional user renames an space or changes the power levels in the meantime. + * could be prevented if data from a sync is piped in this storage as well to keep them always up to date. + */ + + const [cachedRoomNames, setCachedRoomNames] = useState({}); + const [cachedRoomPowerLevels, setCachedRoomPowerLevels] = useState({}); + + /** + * FETCH FUNCTIONS + * ------------------ + * fetch functions to get room specific informations from the current Id + */ + const fetchRoomName = async () => { const nameEvent = await matrixClient.getStateEvent( currentId, 'm.room.name', ).catch(() => {}); setRoomName(nameEvent?.name); + + // send also to the sessions non volatile state storage + setCachedRoomNames({ ...cachedRoomNames, [currentId]: { name: nameEvent?.name } }); }; const fetchRoomTopic = async () => { const topicEvent = await matrixClient.getStateEvent( @@ -130,12 +208,14 @@ const Actions = ({ currentId }) => { currentId, 'm.room.power_levels', ).catch(() => {}); - const modRights = powerLevelsEvent?.users[userId] >= 50; //check if the current user got is listed with a custom power level if true and >= 50 (mod default) mod flag is set true - setUserInfos({ ...userInfos, id: userId, mod: modRights }); - setRoomPowerLevels(powerLevelsEvent?.content); + setUserCurrentIdMod(powerLevelsEvent?.users[userId] >= 50); //check if the current user got is listed with a custom power level if true and >= 50 (mod default) mod flag is set true + setRoomPowerLevels(powerLevelsEvent); + setUserInfos({ ...userInfos, id: userId }); + + // send also to the sessions non volatile state storage + setCachedRoomPowerLevels({ ...cachedRoomPowerLevels, [currentId]: { powerLevels: powerLevelsEvent } }); }; - // Custom State Events const fetchRoomMeta = async () => { const metaEvent = await matrixClient.getStateEvent( currentId, @@ -143,27 +223,40 @@ const Actions = ({ currentId }) => { ).catch(() => {}); setMetaRoom(metaEvent); }; - const fetchCoreInformations = async () => { - await fetchRoomName(); - await fetchRoomTopic(); - await fetchRoomJoinRule(); - await fetchRoomHistoryVisibility(); - await fetchRoomPowerLevels(); - }; + + /** + * RENDER + * ------------------ + * + * | 'ButtonSection' + * 'ActionSection'-> + * | 'MenuSection' --->| 'InfoAction' + * --->| 'AddAction' + * --->| 'RemoveAction' + * --->| 'SettingsAction' + * + * 'ActionSection' acts as an wrapper all of the possible functions + * 'ButtonSection' contains the possible actions which the user can execute, not allowed functiosn for the currently observed Id are disabled. + * for now the following functions are accessable: + * 'info' -> read rights required + * 'add' -> mod rights mostly required + * 'remove' -> mod rights of the parentId required + * 'settings' -> mod/admin rights required + * 'MenuSection' contains the actual user interfaces for the specific action + */ return ( - - - - - + + + + - { showActions.infos && + { showActions === 'info' && //checks if this action got active via a button user input before rendering this component. { getMembers={fetchRoomMembers} getMeta={fetchRoomMeta} /> } - { showActions.add && } - { showActions.remove && } - { showActions.settings && + { showActions === 'settings' && - + = 2 ? activeContexts[activeContexts.length - 2] : undefined} + popActiveContexts={() => {activeContexts.pop();}} + /> ); diff --git a/pages/explore/infoAction.js b/pages/explore/infoAction.js index 89779f49..c14bc318 100644 --- a/pages/explore/infoAction.js +++ b/pages/explore/infoAction.js @@ -16,11 +16,11 @@ const InfoSection = styled.div` `; -/* -* @TODO: -* - adding routes for user interaction more -> members -> username -* - searching for referenced in sync cached rooms (fast) -* - searching for referenced in root tree (slow) +/** + * @TODO: + * - adding routes for user interaction more -> members -> username + * - searching for referenced in sync cached rooms (fast) + * - searching for referenced in root tree (slow) */ const InfoAction = ({ diff --git a/pages/explore/removeAction.js b/pages/explore/removeAction.js index 3d72c25e..0adc5640 100644 --- a/pages/explore/removeAction.js +++ b/pages/explore/removeAction.js @@ -5,38 +5,84 @@ import _ from 'lodash'; import { useAuth } from '../../lib/Auth'; import { useMatrix } from '../../lib/Matrix'; - - import CreateContext from './createContext'; import TemplateSelect from './templateSelect'; -const ModSection = styled.div` +const RemoveSection = styled.div` & { margin-bottom: var(--margin); } & > button { margin-bottom: var(--margin); - } -`; - -const UserSection = styled.div` - & { - margin-bottom: var(--margin); + margin-right: var(--margin); + width:150px; } - & > button { - margin-bottom: var(--margin); + & span:first-of-type { + background-color: var(--color-hi); } `; -const RemoveAction = ({ currentId, parentId, userInfos }) => { +/** + * COMPONENT 'RemoveAction' + * + * @TODO + * - the ContextMultiLevelSelect does not updates completly fine after a remove action is successfully executed. Needs an refresh of the childElements for the Select list. + * + * + * @param {String} currentId — the Id of the current observed Room + * @param {String} parentId — x + * @param {String} name – x + * @param {function} activeAction – x + * @param {function} setShowActions – x +*/ + +const RemoveAction = ({ + currentId, + parentId, + parentPowerLevel, + name, + activeAction, + setShowActions, + popActiveContexts, +}) => { const auth = useAuth(); const matrix = auth.getAuthenticationProvider('matrix'); const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); + const [parentName, setParentName] = useState(); + + const removeChildFromParent = async () => { + const call = await matrix.removeSpaceChild(parentId, currentId); + if (call?.event_id) { //if an event_id is provided as an response the call were accepted and the currentId removed from the parentId as a m.room.spaceChild + setShowActions(''); + popActiveContexts(); + } + }; + + useEffect(() => { + if (activeAction === 'remove') { + fetchParentName(); + } + }, [activeAction, parentId]); + + const fetchParentName = async () => { + const nameEvent = await matrixClient.getStateEvent( + parentId, + 'm.room.name', + ).catch(() => {}); + setParentName(nameEvent?.name); + }; + return ( <> -

          hello :)

          + { (parentName && name) && + ( +

          are you sure you want to remove this? "{ name }" from "{ parentName }"

          + + +
          ) + } ); diff --git a/pages/explore/settingPresets.js b/pages/explore/settingPresets.js index 31f0827f..15cfea73 100644 --- a/pages/explore/settingPresets.js +++ b/pages/explore/settingPresets.js @@ -1,3 +1,4 @@ +import { useTranslation } from 'react-i18next'; // eslint-disable-next-line no-undef module.exports = { name: { From a4d39e017216fea99a7ac9fd909934e395cebf79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Schn=C3=BCll?= Date: Thu, 27 Oct 2022 00:23:33 +0200 Subject: [PATCH 014/714] add 'addExistingContext' to add Action --- pages/explore/AddExistingContext.js | 111 ++++++++++++++++++++++++++++ pages/explore/actions.js | 14 ++-- pages/explore/addAction.js | 19 +++-- 3 files changed, 132 insertions(+), 12 deletions(-) create mode 100644 pages/explore/AddExistingContext.js diff --git a/pages/explore/AddExistingContext.js b/pages/explore/AddExistingContext.js new file mode 100644 index 00000000..53313831 --- /dev/null +++ b/pages/explore/AddExistingContext.js @@ -0,0 +1,111 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import styled from 'styled-components'; +import getConfig from 'next/config'; +import _ from 'lodash'; + +import { useAuth } from '../../lib/Auth'; +import { useMatrix } from '../../lib/Matrix'; +import ContextMultiLevelSelect from '../../components/ContextMultiLevelSelect'; + +const AddContextDialog = styled.div` + & > * + * { + margin-top: var(--margin); + } + & > select + select { + margin-top: calc(var(--margin) * 0.65); + } + & span { + background-color: var(--color-me); + } +`; + +/** + * 'ADD EXISTING CONTEXT' COMPONENT + * ------------------ + * + * @param {String} parentId — x + * @param {String} parentName — x + * @param {function} setShowActions – x + * + * @TODO + * - for now an not nessesary call is executed to get the name of the selected Id to add to the parent context. This information already exisits in the 'ContextMultiLevelSelect' Component, + * but this would need an modification of this component to get this information. Something to implement in the future. + * +*/ + +const AddExistingContext = ({ parentId, parentName, setShowActions }) => { + const auth = useAuth(); + const matrix = auth.getAuthenticationProvider('matrix'); + const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); + + const [activeContexts, setActiveContexts] = useState([getConfig().publicRuntimeConfig.contextRootSpaceRoomId]); + + const [selectedContextName, setSelectedContextName] = useState(); + + const [addAllowed, setAddAllowed] = useState(); + + const [errorMessage, setErrorMessage] = useState(); + + // originated from explore index + useEffect(() => { + const fetchContents = async () => { + const roomHierarchy = await auth.getAuthenticationProvider('matrix').getMatrixClient().getRoomHierarchy(activeContexts[activeContexts.length - 1], undefined, 1); + // Remove the first entry, which is the context itself + roomHierarchy.rooms.shift(); + // Ignore `m.space.child` events that are empty + }; + if (activeContexts) fetchContents(); + fetchRoomName(); + }, [activeContexts, auth]); + + const fetchRoomName = async () => { + const nameEvent = await matrixClient.getStateEvent( + activeContexts[activeContexts.length - 1], + 'm.room.name', + ).catch(() => {}); + setSelectedContextName(nameEvent?.name); + if (parentId && activeContexts[activeContexts.length - 1]) { + setErrorMessage(undefined); + setAddAllowed(true); + } else { + setErrorMessage('At least one of the context is not selected properly'); + setAddAllowed(false); + } + if (parentId && activeContexts[activeContexts.length - 1] && parentId !== activeContexts[activeContexts.length - 1]) { + setErrorMessage(undefined); + setAddAllowed(true); + } else { + setErrorMessage('Not possible to add the same context to itself'); + setAddAllowed(false); + } + }; + + const addContextToParent = async () => { + const call = await matrix.addSpaceChild(parentId, activeContexts[activeContexts.length - 1]); + if (call?.event_id) { + // call worked as expected Dialog can be closed. + cleanUp(); + } else { + setErrorMessage(`matrix server refused to add ${selectedContextName} (${activeContexts[activeContexts.length - 1]}) as a child to ${parentId} (${parentId})`); + } + }; + + // resetting all states back to start for a fresh new interaction. + const cleanUp = () => { + setActiveContexts([getConfig().publicRuntimeConfig.contextRootSpaceRoomId]); + setErrorMessage(undefined); + setShowActions(''); + }; + + return ( + + +

          you are about to add "{ selectedContextName }" to "{ parentName }"

          + + + { errorMessage &&
          ‼️ { errorMessage }
          } +
          + ); +}; + +export default AddExistingContext; diff --git a/pages/explore/actions.js b/pages/explore/actions.js index e48de1c0..ddc353bd 100644 --- a/pages/explore/actions.js +++ b/pages/explore/actions.js @@ -54,7 +54,7 @@ const MenuSection = styled.div` * ------------------ * * @param {String} currentId — the Id of the current observed Room - * @param {String} parentId — the Id of the parent of the currently observed Room + * @param {String} parentId — the Id of the parent of the currently observed Room. Matrix background: parentId lists currentId as an m.space.child stateevent. currentId got no information about the parentId. * @param {function} popActiveContexts – deletes the latest Element of the Contexts Multi Level Select Stack. Needed for the remove action. * */ @@ -117,9 +117,6 @@ const Actions = ({ currentId, parentId, popActiveContexts }) => { } }, [parentId]); - useEffect(() => { - console.log(userInfos?.id); - }, [userParentIdMod]); /** * CURRENT ROOM DATA STORAGE (VOLATILE) * ------------------ @@ -127,8 +124,8 @@ const Actions = ({ currentId, parentId, popActiveContexts }) => { * this can be populated seperatatly to by user interactions and will not preload all informations at the beginnning * this approach was chosen instead of just getting all stateEvents of the room for performance reason as it contains a stateEvent for each user, * which will increase the requested pauload with a couple of thousends a lot. - * sadly as react evolves into this weird directions, the declaration of the variables needed to be implemented like this; - * in an non nested way, feels like written by a 5 year old. + * sadly as react evolves into this weird directions, the declaration of the variables needed to be implemented like this: + * not in a nested way; feels like written by a 5 year old. */ const [roomName, setRoomName] = useState(); @@ -186,6 +183,8 @@ const Actions = ({ currentId, parentId, popActiveContexts }) => { }; }); members.institutions = _.uniq(_.map(members.list, member => member?.id.split(':')[1])); + // insitutions just holds the names of the matrix homeservers which the joined members orignated from. + // will become more relevant in the future if federatable networks will be implemented. for now its just an non interactive list of strings setRoomMembers(members); }; const fetchRoomJoinRule = async () => { @@ -273,6 +272,9 @@ const Actions = ({ currentId, parentId, popActiveContexts }) => { } { showActions === 'remove' && { +const AddAction = ({ currentId, userInfos, mod, currentName,setShowActions }) => { const auth = useAuth(); const matrix = auth.getAuthenticationProvider('matrix'); const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); @@ -40,10 +39,18 @@ const AddAction = ({ currentId, userInfos }) => { - { userInfos?.mod? //if no mod rights are granted for this current Id this section will not be displayed + { mod? //if no mod rights are granted for this current Id this section will not be displayed - - +
          + create new substructure + +
          + +
          + add existing context from root + +
          +
          : <> } From 4cb44fc691f3963ba06a3b58285420a065020e8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Schn=C3=BCll?= Date: Thu, 27 Oct 2022 12:39:43 +0200 Subject: [PATCH 015/714] css linter fixes --- pages/explore/AddExistingContext.js | 20 ++++++----- pages/explore/actions.js | 25 ++++++-------- pages/explore/addAction.js | 40 ++++++++++++++-------- pages/explore/createContext.js | 52 ++--------------------------- pages/explore/index.js | 1 + pages/explore/infoAction.js | 4 +-- pages/explore/removeAction.js | 23 +++++++------ pages/explore/settingsAction.js | 3 +- pages/explore/userHandle.js | 13 ++++---- 9 files changed, 75 insertions(+), 106 deletions(-) diff --git a/pages/explore/AddExistingContext.js b/pages/explore/AddExistingContext.js index 53313831..33a902c0 100644 --- a/pages/explore/AddExistingContext.js +++ b/pages/explore/AddExistingContext.js @@ -8,15 +8,17 @@ import { useMatrix } from '../../lib/Matrix'; import ContextMultiLevelSelect from '../../components/ContextMultiLevelSelect'; const AddContextDialog = styled.div` - & > * + * { - margin-top: var(--margin); - } - & > select + select { - margin-top: calc(var(--margin) * 0.65); - } - & span { - background-color: var(--color-me); - } + & > * + * { + margin-top: var(--margin); + } + + & > select + select { + margin-top: calc(var(--margin) * 0.65); + } + + & span { + background-color: var(--color-me); + } `; /** diff --git a/pages/explore/actions.js b/pages/explore/actions.js index ddc353bd..4038caec 100644 --- a/pages/explore/actions.js +++ b/pages/explore/actions.js @@ -11,41 +11,38 @@ import AddAction from './addAction'; import RemoveAction from './removeAction'; const ActionsSection = styled.details` - & { + & { margin-bottom: var(--margin); - } + & > summary { - background-color: var(--color-fg); - color: var(--color-bg); width: 50px; height: 35px; - list-style: none; + font-weight: bold; + color: var(--color-bg); text-align: center; - font-weight:bold; + list-style: none; + background-color: var(--color-fg); } & > div > button { - margin-bottom: var(--margin); + width: 50px; margin-top: var(--margin); margin-right: var(--margin); - width: 50px; - + margin-bottom: var(--margin); } `; const ButtonsSection = styled.div` - & { + & { margin-bottom: var(--margin); - } `; const MenuSection = styled.div` - & { + & { margin-bottom: var(--margin); - } `; @@ -183,7 +180,7 @@ const Actions = ({ currentId, parentId, popActiveContexts }) => { }; }); members.institutions = _.uniq(_.map(members.list, member => member?.id.split(':')[1])); - // insitutions just holds the names of the matrix homeservers which the joined members orignated from. + // institutions just holds the names of the matrix homeservers which the joined members orignated from. // will become more relevant in the future if federatable networks will be implemented. for now its just an non interactive list of strings setRoomMembers(members); }; diff --git a/pages/explore/addAction.js b/pages/explore/addAction.js index 884fbfb7..2f45e94e 100644 --- a/pages/explore/addAction.js +++ b/pages/explore/addAction.js @@ -10,25 +10,39 @@ import TemplateSelect from './templateSelect'; import AddExistingContext from './AddExistingContext'; const ModSection = styled.div` - & { - margin-bottom: var(--margin); - } - & > button { - margin-bottom: var(--margin); - } + & { + margin-bottom: var(--margin); + } + + & > button { + margin-bottom: var(--margin); + } `; const UserSection = styled.div` - & { - margin-bottom: var(--margin); - } + & { + margin-bottom: var(--margin); + } - & > button { - margin-bottom: var(--margin); - } + & > button { + margin-bottom: var(--margin); + } `; -const AddAction = ({ currentId, userInfos, mod, currentName,setShowActions }) => { +/** + * ACTIONS COMPONENT + * ------------------ + * + * @param {String} currentId — the Id of the current observed Room + * @param {String} parentId — the Id of the parent of the currently observed Room. Matrix background: parentId lists currentId as an m.space.child stateevent. currentId got no information about the parentId. + * @param {function} popActiveContexts – deletes the latest Element of the Contexts Multi Level Select Stack. Needed for the remove action. + * + * @TODO + * - add item function with integration of existing application spaces, like write. + * - add joined contexts +*/ + +const AddAction = ({ currentId, userInfos, mod, currentName, setShowActions }) => { const auth = useAuth(); const matrix = auth.getAuthenticationProvider('matrix'); const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); diff --git a/pages/explore/createContext.js b/pages/explore/createContext.js index ff0e98bd..1c7476e4 100644 --- a/pages/explore/createContext.js +++ b/pages/explore/createContext.js @@ -17,56 +17,10 @@ const CreateContext = ({ currentId }) => { const matrix = auth.getAuthenticationProvider('matrix'); const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); - - useEffect(() => { // basic input validation for the input fields to create a new substructure - if (createNew.name === '' || createNew?.name.length < 4) { - createNew.interfaceError = 'name too short'; - return; - } else { - createNew.interfaceError = ''; - } - - if (createNew.template === '' || createNew?.template.length < 4) { - createNew.interfaceError = 'template name too short'; - return; - } else { - createNew.interfaceError = ''; - } - }, [createNew]); - - const createContext = (e) => { - e.preventDefault(); - if (currentId.length > 10 && currentId.charAt(0) === '!' && currentId.includes(':')) { //just some really really basic check if it could be an matrix id - setCreateNew({ ...createNew, parent: currentId }); - console.log('created'); - } else { - createNew.interfaceError = 'something went wrong with the selected matrix Id, please reload'; - } - }; - return ( - getTemplatesOfContexts(getConfig().publicRuntimeConfig.templates.context)}> { /* will only load the templates after expanding this view to prevent unnecessary network traffic */ } - create Substructure -
          - - -
          - advanced - - - -
          - - { createNew.interfaceError &&

          ‼️ { createNew.interfaceError }

          } { /* Showing the current Error to the user if some input validation failed */ } - -
          -
          + <> + hello :) + ); }; diff --git a/pages/explore/index.js b/pages/explore/index.js index 3d19f5a5..9fbbc206 100644 --- a/pages/explore/index.js +++ b/pages/explore/index.js @@ -11,6 +11,7 @@ const ExploreSection = styled.div` & > * + * { margin-top: var(--margin); } + & > select + select { margin-top: calc(var(--margin) * 0.65); } diff --git a/pages/explore/infoAction.js b/pages/explore/infoAction.js index c14bc318..94152bdd 100644 --- a/pages/explore/infoAction.js +++ b/pages/explore/infoAction.js @@ -8,12 +8,10 @@ import { useMatrix } from '../../lib/Matrix'; import UserHandle from './userHandle'; const InfoSection = styled.div` - & { + & { margin-bottom: var(--margin); - } - `; /** diff --git a/pages/explore/removeAction.js b/pages/explore/removeAction.js index 0adc5640..cf048688 100644 --- a/pages/explore/removeAction.js +++ b/pages/explore/removeAction.js @@ -9,18 +9,19 @@ import CreateContext from './createContext'; import TemplateSelect from './templateSelect'; const RemoveSection = styled.div` - & { - margin-bottom: var(--margin); - } - & > button { - margin-bottom: var(--margin); - margin-right: var(--margin); - width:150px; - } + & { + margin-bottom: var(--margin); + } - & span:first-of-type { - background-color: var(--color-hi); - } + & > button { + width: 150px; + margin-right: var(--margin); + margin-bottom: var(--margin); + } + + & span:first-of-type { + background-color: var(--color-hi); + } `; /** diff --git a/pages/explore/settingsAction.js b/pages/explore/settingsAction.js index ba8accc2..32647a51 100644 --- a/pages/explore/settingsAction.js +++ b/pages/explore/settingsAction.js @@ -13,7 +13,8 @@ const SettingsSection = styled.div` margin-bottom: var(--margin); } - & input, select { + & input, + select { margin-bottom: var(--margin); } `; diff --git a/pages/explore/userHandle.js b/pages/explore/userHandle.js index d5457de9..29b82a72 100644 --- a/pages/explore/userHandle.js +++ b/pages/explore/userHandle.js @@ -2,12 +2,13 @@ import React, { useCallback, useEffect, useState } from 'react'; import styled from 'styled-components'; const User = styled.div` - & { - margin-bottom: var(--margin); - } - & > button { - margin-bottom: var(--margin); - } + & { + margin-bottom: var(--margin); + } + + & > button { + margin-bottom: var(--margin); + } `; const UserHandle = ({ userId }) => { From 2db8322f6df068dc026f1c1d0c09373c18dd01e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Schn=C3=BCll?= Date: Thu, 27 Oct 2022 12:51:14 +0200 Subject: [PATCH 016/714] Update userHandle.js --- pages/explore/userHandle.js | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/pages/explore/userHandle.js b/pages/explore/userHandle.js index 29b82a72..47c5d167 100644 --- a/pages/explore/userHandle.js +++ b/pages/explore/userHandle.js @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useState } from 'react'; +import React from 'react'; import styled from 'styled-components'; const User = styled.div` @@ -11,15 +11,28 @@ const User = styled.div` } `; -const UserHandle = ({ userId }) => { +/** + * UserHandle COMPONENT + * ------------------ + * + * @param {String} userId — the Id of the current observed Room + * + * @TODO + * - adding user interaction: sending dm. url parameter scheme for /chat needs to be ready before implementing this to forward to that url after sending an invite. + * - adding user interaction: invite to… context, but also to application items, needs further disucssion before specify the requirements for an implementation. + * - adding user interaction: contextualize…. this should show contexts, items and so on which the current loggedIn user as well as the observed user are both part of. +*/ +const UserHandle = ({ userId }) => { return (
          { userId?.displayname ? userId?.displayname : userId?.id.split(':')[0].substring(1) } { /* If Displayname is not set fallback to user id */ } + { /*

          send dm

          invite to…

          contextualize…

          + */ }
          ); From 6e67d7d4f9aeced200047a21bdcd3d17c934759b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Schn=C3=BCll?= Date: Thu, 27 Oct 2022 13:02:30 +0200 Subject: [PATCH 017/714] restructuring folders --- pages/explore/{addAction.js => actions/AddAction.js} | 10 +++++----- .../explore/{infoAction.js => actions/InfoAction.js} | 6 +++--- .../{removeAction.js => actions/RemoveAction.js} | 8 ++++---- .../{settingsAction.js => actions/SettingsAction.js} | 8 ++++---- pages/explore/{actions.js => actions/index.js} | 12 ++++++------ pages/explore/{ => actions}/settingPresets.js | 0 pages/explore/userHandle.js | 2 ++ 7 files changed, 24 insertions(+), 22 deletions(-) rename pages/explore/{addAction.js => actions/AddAction.js} (89%) rename pages/explore/{infoAction.js => actions/InfoAction.js} (97%) rename pages/explore/{removeAction.js => actions/RemoveAction.js} (92%) rename pages/explore/{settingsAction.js => actions/SettingsAction.js} (97%) rename pages/explore/{actions.js => actions/index.js} (98%) rename pages/explore/{ => actions}/settingPresets.js (100%) diff --git a/pages/explore/addAction.js b/pages/explore/actions/AddAction.js similarity index 89% rename from pages/explore/addAction.js rename to pages/explore/actions/AddAction.js index 2f45e94e..5bfc4529 100644 --- a/pages/explore/addAction.js +++ b/pages/explore/actions/AddAction.js @@ -3,11 +3,11 @@ import styled from 'styled-components'; import getConfig from 'next/config'; import _ from 'lodash'; -import { useAuth } from '../../lib/Auth'; -import { useMatrix } from '../../lib/Matrix'; -import CreateContext from './createContext'; -import TemplateSelect from './templateSelect'; -import AddExistingContext from './AddExistingContext'; +import { useAuth } from '../../../lib/Auth'; +import { useMatrix } from '../../../lib/Matrix'; +import CreateContext from '../CreateContext'; +import TemplateSelect from '../TemplateSelect'; +import AddExistingContext from '../AddExistingContext'; const ModSection = styled.div` & { diff --git a/pages/explore/infoAction.js b/pages/explore/actions/InfoAction.js similarity index 97% rename from pages/explore/infoAction.js rename to pages/explore/actions/InfoAction.js index 94152bdd..f27441e8 100644 --- a/pages/explore/infoAction.js +++ b/pages/explore/actions/InfoAction.js @@ -3,9 +3,9 @@ import styled from 'styled-components'; import getConfig from 'next/config'; import _ from 'lodash'; -import { useAuth } from '../../lib/Auth'; -import { useMatrix } from '../../lib/Matrix'; -import UserHandle from './userHandle'; +import { useAuth } from '../../../lib/Auth'; +import { useMatrix } from '../../../lib/Matrix'; +import UserHandle from '../UserHandle'; const InfoSection = styled.div` & { diff --git a/pages/explore/removeAction.js b/pages/explore/actions/RemoveAction.js similarity index 92% rename from pages/explore/removeAction.js rename to pages/explore/actions/RemoveAction.js index cf048688..0cc93b4a 100644 --- a/pages/explore/removeAction.js +++ b/pages/explore/actions/RemoveAction.js @@ -3,10 +3,10 @@ import styled from 'styled-components'; import getConfig from 'next/config'; import _ from 'lodash'; -import { useAuth } from '../../lib/Auth'; -import { useMatrix } from '../../lib/Matrix'; -import CreateContext from './createContext'; -import TemplateSelect from './templateSelect'; +import { useAuth } from '../../../lib/Auth'; +import { useMatrix } from '../../../lib/Matrix'; +import CreateContext from '../CreateContext'; +import TemplateSelect from '../TemplateSelect'; const RemoveSection = styled.div` & { diff --git a/pages/explore/settingsAction.js b/pages/explore/actions/SettingsAction.js similarity index 97% rename from pages/explore/settingsAction.js rename to pages/explore/actions/SettingsAction.js index 32647a51..b7341603 100644 --- a/pages/explore/settingsAction.js +++ b/pages/explore/actions/SettingsAction.js @@ -2,10 +2,10 @@ import React, { useEffect, useState } from 'react'; import styled from 'styled-components'; import _ from 'lodash'; -import TemplateSelect from './templateSelect'; -import UserHandle from './userHandle'; -import { useAuth } from '../../lib/Auth'; -import { useMatrix } from '../../lib/Matrix'; +import TemplateSelect from '../TemplateSelect'; +import UserHandle from '../UserHandle'; +import { useAuth } from '../../../lib/Auth'; +import { useMatrix } from '../../../lib/Matrix'; import settingsPresets from './settingPresets'; const SettingsSection = styled.div` diff --git a/pages/explore/actions.js b/pages/explore/actions/index.js similarity index 98% rename from pages/explore/actions.js rename to pages/explore/actions/index.js index 4038caec..03089517 100644 --- a/pages/explore/actions.js +++ b/pages/explore/actions/index.js @@ -3,12 +3,12 @@ import styled from 'styled-components'; import getConfig from 'next/config'; import _ from 'lodash'; -import { useAuth } from '../../lib/Auth'; -import { useMatrix } from '../../lib/Matrix'; -import SettingsAction from './settingsAction'; -import InfoAction from './infoAction'; -import AddAction from './addAction'; -import RemoveAction from './removeAction'; +import { useAuth } from '../../../lib/Auth'; +import { useMatrix } from '../../../lib/Matrix'; +import SettingsAction from './SettingsAction'; +import InfoAction from './InfoAction'; +import AddAction from './AddAction'; +import RemoveAction from './RemoveAction'; const ActionsSection = styled.details` & { diff --git a/pages/explore/settingPresets.js b/pages/explore/actions/settingPresets.js similarity index 100% rename from pages/explore/settingPresets.js rename to pages/explore/actions/settingPresets.js diff --git a/pages/explore/userHandle.js b/pages/explore/userHandle.js index 47c5d167..307e8e83 100644 --- a/pages/explore/userHandle.js +++ b/pages/explore/userHandle.js @@ -14,6 +14,8 @@ const User = styled.div` /** * UserHandle COMPONENT * ------------------ + * This Component should be uses as an global Component for optional user interaction. + * It should be used through the whole space application each time a username/ userId is rendered. * * @param {String} userId — the Id of the current observed Room * From 44e955ad84bde6293479c27c19a2a8e41030fd55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Schn=C3=BCll?= Date: Mon, 31 Oct 2022 16:57:42 +0100 Subject: [PATCH 018/714] adding templateSelect component --- pages/explore/AdvancedRoomOptions.js | 57 +++++++ pages/explore/actions/AddAction.js | 7 +- pages/explore/actions/InfoAction.js | 21 +-- pages/explore/actions/SettingsAction.js | 2 +- pages/explore/actions/index.js | 2 + pages/explore/createContext.js | 29 ++-- .../{actions/settingPresets.js => presets.js} | 0 pages/explore/templateSelect.js | 147 ++++++++++++++---- pages/explore/userHandle.js | 6 +- 9 files changed, 223 insertions(+), 48 deletions(-) create mode 100644 pages/explore/AdvancedRoomOptions.js rename pages/explore/{actions/settingPresets.js => presets.js} (100%) diff --git a/pages/explore/AdvancedRoomOptions.js b/pages/explore/AdvancedRoomOptions.js new file mode 100644 index 00000000..269dc4fa --- /dev/null +++ b/pages/explore/AdvancedRoomOptions.js @@ -0,0 +1,57 @@ +import React, { useCallback, useEffect, useState } from 'react'; +import styled from 'styled-components'; +import getConfig from 'next/config'; +import _ from 'lodash'; + +import { useAuth } from '../../lib/Auth'; +import { useMatrix } from '../../lib/Matrix'; + +import TemplateSelect from './TemplateSelect'; + +/** + * ADVANCED ROOM MODERATE OPTIONS COMPONENT + * ------------------ + * + * @param {String} currentId – x + * @param {String} currentOptions – x + * @param {String} userInfos – x + * @param {String} powerLevels – x + * @param {String} presets – x + * + * This component is used in the 'settings' action component as well as in the 'create context' component. + * + * @TODO + * - in the future more advanced options will be added which are not matrix specification based. as example adding of temporal or allocation data, both part of the 'dev.medienhaus' custom state events +*/ + + +const AdvancedRoomOptions = ({ currentId, currentOptions, powerLevels, presets }) => { + const auth = useAuth(); + const matrix = auth.getAuthenticationProvider('matrix'); + const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); + + return ( + <> + + + + + ); +}; + +export default AdvancedRoomOptions; diff --git a/pages/explore/actions/AddAction.js b/pages/explore/actions/AddAction.js index 5bfc4529..369cc462 100644 --- a/pages/explore/actions/AddAction.js +++ b/pages/explore/actions/AddAction.js @@ -57,7 +57,7 @@ const AddAction = ({ currentId, userInfos, mod, currentName, setShowActions }) =
          create new substructure - +
          @@ -65,6 +65,11 @@ const AddAction = ({ currentId, userInfos, mod, currentName, setShowActions }) =
          +
          + add existing item from application + <> +
          +
          : <> } diff --git a/pages/explore/actions/InfoAction.js b/pages/explore/actions/InfoAction.js index f27441e8..e03ee8e9 100644 --- a/pages/explore/actions/InfoAction.js +++ b/pages/explore/actions/InfoAction.js @@ -15,8 +15,10 @@ const InfoSection = styled.div` `; /** - * @TODO: - * - adding routes for user interaction more -> members -> username + * INFO ACTION COMPONENT + * ------------------ + * @TODO + * - adding routes for user interaction 'more' -> 'members' -> 'username'; now been done in the UserHandle Component * - searching for referenced in sync cached rooms (fast) * - searching for referenced in root tree (slow) */ @@ -100,7 +102,7 @@ const InfoAction = ({
          { meta?.template }
      -
      0 ? undefined : getMembers}> +
      0 ? undefined : getMembers}> {/* check first if members are already fetched once to prevent unessesary calls */} Institutions
        { _.map(members?.institutions, (institution, key) => { @@ -129,15 +131,16 @@ const InfoAction = ({ }
      -
      - Referenced - … -
      - + {/* +
      + Referenced + … +
      + */}
      ); }; -export default InfoAction; +export default InfoAction; \ No newline at end of file diff --git a/pages/explore/actions/SettingsAction.js b/pages/explore/actions/SettingsAction.js index b7341603..7b192dae 100644 --- a/pages/explore/actions/SettingsAction.js +++ b/pages/explore/actions/SettingsAction.js @@ -6,7 +6,7 @@ import TemplateSelect from '../TemplateSelect'; import UserHandle from '../UserHandle'; import { useAuth } from '../../../lib/Auth'; import { useMatrix } from '../../../lib/Matrix'; -import settingsPresets from './settingPresets'; +import settingsPresets from '../presets'; const SettingsSection = styled.div` & { diff --git a/pages/explore/actions/index.js b/pages/explore/actions/index.js index 03089517..17895464 100644 --- a/pages/explore/actions/index.js +++ b/pages/explore/actions/index.js @@ -54,6 +54,8 @@ const MenuSection = styled.div` * @param {String} parentId — the Id of the parent of the currently observed Room. Matrix background: parentId lists currentId as an m.space.child stateevent. currentId got no information about the parentId. * @param {function} popActiveContexts – deletes the latest Element of the Contexts Multi Level Select Stack. Needed for the remove action. * + * @TODO + * - changing all hardcoded mod rights (50) in all files related to the 'action' component to dynamicly ones. so that it will check what the powerlevel for the intended event to send needs to be, based on the indidual specific room criterial. */ const Actions = ({ currentId, parentId, popActiveContexts }) => { diff --git a/pages/explore/createContext.js b/pages/explore/createContext.js index 1c7476e4..29ae44c7 100644 --- a/pages/explore/createContext.js +++ b/pages/explore/createContext.js @@ -6,21 +6,32 @@ import _ from 'lodash'; import { useAuth } from '../../lib/Auth'; import { useMatrix } from '../../lib/Matrix'; -const CreateSubstructureSection = styled.details` - & { - margin-bottom: var(--margin); - } -`; +import TemplateSelect from './TemplateSelect'; +import AdvancedRoomOptions from './AdvancedRoomOptions' +import presets from './presets'; -const CreateContext = ({ currentId }) => { + +const CreateContext = ({ currentId, userInfos }) => { const auth = useAuth(); const matrix = auth.getAuthenticationProvider('matrix'); const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); return ( - <> - hello :) - +
      + + + +
      + Advanced + +
      + + + ); }; diff --git a/pages/explore/actions/settingPresets.js b/pages/explore/presets.js similarity index 100% rename from pages/explore/actions/settingPresets.js rename to pages/explore/presets.js diff --git a/pages/explore/templateSelect.js b/pages/explore/templateSelect.js index 4c1af312..46af9b26 100644 --- a/pages/explore/templateSelect.js +++ b/pages/explore/templateSelect.js @@ -1,52 +1,141 @@ import React, { useCallback, useEffect, useState } from 'react'; import styled from 'styled-components'; -import getConfig from 'next/config'; import _ from 'lodash'; import { useAuth } from '../../lib/Auth'; import { useMatrix } from '../../lib/Matrix'; -const TemplateSelect = ({ currentId }) => { + +/** + * TEMPLATE SELECT COMPONENT + * ------------------ + * + * @param {String} currentId — the Id of the current observed Room + * @param {String} templateDirectoryId — the Id of the Room to fetch the templates from + * + * @TODO + * - piping sync functionallity in this component so that the content of the template room will not be requested each time, without any content has changed. + * - adding new contents in templateDirectoryRoom +*/ + +const TemplateSelect = ({ currentId, templateDirectoryId,userInfos }) => { const auth = useAuth(); const matrix = auth.getAuthenticationProvider('matrix'); const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); - const [createNew, setCreateNew] = useState({ name: '', template: '', parent: '', interfaceError: '' }); //stores all nessesarry user input from the form - const [generateNewTemplate, setGenerateNewTemplate] = useState(false); + const [fetchedTemplates, setFetchedTemplates] = useState([]); //hold all of the templates from the templateId in chache + + const [selectedTemplate,setSelectedTemplate] = useState(); + + // States to create a new Template + const [createNewDialog,setCreateNewDialog] = useState(false); + const [createNewTemplateName,setCreateNewTemplateName] = useState(''); + const [createNewTemplateErrorMessage,setCreateNewTemplateErrorMessage] = useState('') - // Logics for the functions of the 'CreateSubstructureSection' - // includes some basic input validation and dynamic form adjustments for the template selection - const [contextTemplates, setContextTemplates] = useState([]); - async function getTemplatesOfContexts(roomId) { // gets the templates from the - if (contextTemplates.length > 0) return; + + useEffect(() => { + getTemplatesOfContexts(templateDirectoryId) + setCreateNewDialog(false) + }, [currentId,setCreateNewDialog]); //fetches all of the templates from the given Id when this Component is instantiated after the selected observed Id changed + + + const getTemplatesOfContexts = async (roomId) => { // gets the templates from the Id const metaEvent = await auth.getAuthenticationProvider('matrix').getMatrixClient().getStateEvent(roomId, 'dev.medienhaus.meta').catch(() => {}); if ((metaEvent?.template !== 'templates')) return; console.log(metaEvent); - const roomContent = await (matrix.fetchRoomMessages(roomId, 5)); + const roomContent = await (matrix.fetchRoomMessages(roomId, 50)); const templates = _.uniq(roomContent?.chunk.map(entry => entry?.content?.body)).filter(e => e); - setContextTemplates(templates); + setFetchedTemplates(templates); } - const createNewChangeHandle = (e) => { - if (e.target.name === 'template' && e.target.value === '_createNew') {//check if dropdown is selected for new to modify form - e.target.value === '_createNew'? setGenerateNewTemplate(true) : setGenerateNewTemplate(false); - setCreateNew({ ...createNew, template: '' }); - return; - } - if (e.target.name === 'newTemplate') { - setCreateNew({ ...createNew, template: e.target.value }); + const onChangeTemplateSelect = (e) => { + if (e.target.value === 'template') return + setSelectedTemplate(e) + if(e.target.value === '_createNew') { + setCreateNewDialog(true) } else { - setCreateNew({ ...createNew, [e.target.name]: e.target.value }); //this is the regular way if no errors occured before + setCreateNewDialog(false) } }; + const onSaveCreateNewTemplate = async () => { + /** + * @TODO + * ✔️ checking if user is member of room + * ✔️ checking if user is allowed to post message in the room otherwise throw error + * ✔️ posting message with new template to the room + * ✔️ return positive response and empty errormesssage if event_id is returned by matrix server + * - generalising this functionallity of 'checking if in room, checking if allowed to post, post message' and adding it to the matrixAuthProvider + * - some error occured getJoinedRoomMembers is fetched and the user is not part of the room. M_UNKNOWN with 500 returned. + */ + + const members = await matrixClient.getJoinedRoomMembers(templateDirectoryId) + + if(!(userInfos?.id in members?.joined)) { //checking if current loggedIn User is already member of the room, if not condition is triggered + console.log('not member, trying to join') + const joinRequest = await matrixClient.joinRoom(templateDirectoryId) + if(joinRequest?.roomId !== templateDirectoryId) { //if not joined successful then throw error + setCreateNewTemplateErrorMessage("now allowed to join the template directory room Id") + return + } + } + + //checking rights if allowed to post + const powerLevels = await matrixClient.getStateEvent(templateDirectoryId, 'm.room.power_levels').catch(() => {}); + console.log(powerLevels?.users[userInfos?.id] ) + if( ( powerLevels?.users[userInfos?.id] >= powerLevels?.events_default) || powerLevels?.events_default === powerLevels?.users_default ) { + //user is allowed to send messages + const sendMessage = await matrixClient.sendMessage(templateDirectoryId,{"msgtype":"m.text","body":createNewTemplateName}) + if(sendMessage?.event_id) { // everything worked out as expected :) closing create new template dialoge, reset everything and selecting the new option + setCreateNewTemplateErrorMessage("") + getTemplatesOfContexts(templateDirectoryId) + setCreateNewDialog(false) + setSelectedTemplate(createNewTemplateName) + } else { + setCreateNewTemplateErrorMessage("something went wrong") + } + } else { + setCreateNewTemplateErrorMessage("now allowed to submit new templates to the room Id") + return + } + + + + } + + const onChangeCreateNewTemplate = (e) => { + setCreateNewTemplateName(e.target.value) + if(e.target.value.length < 4){ + setCreateNewTemplateErrorMessage("template needs to contain at least 4 character") + return + } else { + setCreateNewTemplateErrorMessage("") + } + if(fetchedTemplates.includes(e.target.value)) { + setCreateNewTemplateErrorMessage("template already exists") + return + } else { + setCreateNewTemplateErrorMessage("") + } + } + + const changeTemplateInMetaEventOfCurrentId = async () => { + /** + * @TODO + * - checking if user is member of room + * - checking if user is allowed to change the dev.medienhaus.meta stateEvent in the room otherwise throw error + */ + } + + + return ( <> - + + { fetchedTemplates.map((template, key) => { {/* cycle through all of the collective specified templates stored in a matrix room */} return { /* static element for users to create a new template */ } - { generateNewTemplate && }{ /* this input is only generated if the dropwdown was selected 'create new…' */ } + { createNewDialog && + <> + + + + }{ /* this input is only generated if the dropwdown was selected 'create new…' */ } + { (createNewTemplateErrorMessage) && +
      ‼️ {createNewTemplateErrorMessage}
      //error message container + } - ); }; - export default TemplateSelect; diff --git a/pages/explore/userHandle.js b/pages/explore/userHandle.js index 307e8e83..b98acb19 100644 --- a/pages/explore/userHandle.js +++ b/pages/explore/userHandle.js @@ -12,7 +12,7 @@ const User = styled.div` `; /** - * UserHandle COMPONENT + * USERHANDLE COMPONENT * ------------------ * This Component should be uses as an global Component for optional user interaction. * It should be used through the whole space application each time a username/ userId is rendered. @@ -30,7 +30,9 @@ const UserHandle = ({ userId }) => {
      { userId?.displayname ? userId?.displayname : userId?.id.split(':')[0].substring(1) } { /* If Displayname is not set fallback to user id */ } - { /* + { + <> + /*

      send dm

      invite to…

      contextualize…

      From 7a590077eec7daef5ec0d35dede723809c031ccc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Schn=C3=BCll?= Date: Tue, 1 Nov 2022 16:31:09 +0100 Subject: [PATCH 019/714] fixing tempateSelect Bugs --- pages/explore/actions/AddAction.js | 2 +- pages/explore/templateSelect.js | 29 ++++++++++++++++++----------- 2 files changed, 19 insertions(+), 12 deletions(-) diff --git a/pages/explore/actions/AddAction.js b/pages/explore/actions/AddAction.js index 369cc462..72014490 100644 --- a/pages/explore/actions/AddAction.js +++ b/pages/explore/actions/AddAction.js @@ -57,7 +57,7 @@ const AddAction = ({ currentId, userInfos, mod, currentName, setShowActions }) =
      create new substructure - +
      diff --git a/pages/explore/templateSelect.js b/pages/explore/templateSelect.js index 46af9b26..4160ae78 100644 --- a/pages/explore/templateSelect.js +++ b/pages/explore/templateSelect.js @@ -12,13 +12,17 @@ import { useMatrix } from '../../lib/Matrix'; * * @param {String} currentId — the Id of the current observed Room * @param {String} templateDirectoryId — the Id of the Room to fetch the templates from + * @param {String} userInfos — x + * @param {Function} currentTemplate — x + * @param {Boolean} updateCurrentId – x * * @TODO * - piping sync functionallity in this component so that the content of the template room will not be requested each time, without any content has changed. * - adding new contents in templateDirectoryRoom + * - fixing same bugs to save a new template todo in 'onSaveCreateNewTemplate' */ -const TemplateSelect = ({ currentId, templateDirectoryId,userInfos }) => { +const TemplateSelect = ({ currentId, templateDirectoryId, userInfos, currentTemplate }) => { const auth = useAuth(); const matrix = auth.getAuthenticationProvider('matrix'); const matrixClient = auth.getAuthenticationProvider('matrix').getMatrixClient(); @@ -34,6 +38,17 @@ const TemplateSelect = ({ currentId, templateDirectoryId,userInfos }) => { + useEffect(() => { + //checking if currentTemplate is a function + if (!currentTemplate) return + // checking if 'selectedTemplate' is + if(selectedTemplate !== '_createNew') { + currentTemplate(selectedTemplate) + } else { + currentTemplate('') + } + }, [selectedTemplate]); + useEffect(() => { getTemplatesOfContexts(templateDirectoryId) @@ -52,7 +67,7 @@ const TemplateSelect = ({ currentId, templateDirectoryId,userInfos }) => { const onChangeTemplateSelect = (e) => { if (e.target.value === 'template') return - setSelectedTemplate(e) + setSelectedTemplate(e.target.value) if(e.target.value === '_createNew') { setCreateNewDialog(true) } else { @@ -121,14 +136,6 @@ const TemplateSelect = ({ currentId, templateDirectoryId,userInfos }) => { } } - const changeTemplateInMetaEventOfCurrentId = async () => { - /** - * @TODO - * - checking if user is member of room - * - checking if user is allowed to change the dev.medienhaus.meta stateEvent in the room otherwise throw error - */ - } - return ( @@ -141,7 +148,7 @@ const TemplateSelect = ({ currentId, templateDirectoryId,userInfos }) => { { template } ; }) } - { /* static element for users to create a new template */ } + { /* static element for users to create a new template */ } { createNewDialog && <> From f1fad332c026c33a46266aef663c2be255a414eb Mon Sep 17 00:00:00 2001 From: Marcel Date: Wed, 11 Jan 2023 16:54:09 +0100 Subject: [PATCH 020/714] remove color variable from merging --- components/layouts/_base.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/components/layouts/_base.js b/components/layouts/_base.js index 800045ae..4ece060f 100644 --- a/components/layouts/_base.js +++ b/components/layouts/_base.js @@ -13,8 +13,6 @@ const Wrapper = styled.div` height: 100vh; overflow: ${props => props.navigationopen ? 'hidden' : 'unset'}; - --color-background-sidebar: rgb(240 240 240); - @media ${breakpoints.tabletAndAbove} { display: grid; grid-template-rows: min-content 1fr min-content; From 0c2a1aa38611d0886cbe010379e1f30d14e2db41 Mon Sep 17 00:00:00 2001 From: Marcel Date: Wed, 25 Jan 2023 14:25:00 +0100 Subject: [PATCH 021/714] update matrix-js-sdk --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 796663ae..f90dbdf7 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "i18next-browser-languagedetector": "^6.1.4", "i18next-resources-to-backend": "^1.0.0", "lodash": "^4.17.21", - "matrix-js-sdk": "^16.0.0", + "matrix-js-sdk": "^23.1.1", "next": "^12.1.0", "normalize.css": "^8.0.1", "react": "^17.0.2", From b14be56bb52e2276f66b17afc5d292c568e75dbf Mon Sep 17 00:00:00 2001 From: Marcel Date: Wed, 25 Jan 2023 17:17:28 +0100 Subject: [PATCH 022/714] revert back to js sdk 16 because of problems with new version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f90dbdf7..796663ae 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "i18next-browser-languagedetector": "^6.1.4", "i18next-resources-to-backend": "^1.0.0", "lodash": "^4.17.21", - "matrix-js-sdk": "^23.1.1", + "matrix-js-sdk": "^16.0.0", "next": "^12.1.0", "normalize.css": "^8.0.1", "react": "^17.0.2", From 5c53aaa7cad7322b65669ac727de174f83a76fba Mon Sep 17 00:00:00 2001 From: Marcel Date: Fri, 27 Jan 2023 18:21:24 +0100 Subject: [PATCH 023/714] add d3js --- package-lock.json | 706 +++++++++++++++++++++++++++++++++++++++++++++- package.json | 3 +- 2 files changed, 705 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index 034e5668..82fe63b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6,6 +6,7 @@ "": { "name": "medienhaus-frontend", "dependencies": { + "d3": "^7.8.2", "i18next": "^21.6.14", "i18next-browser-languagedetector": "^6.1.4", "i18next-resources-to-backend": "^1.0.0", @@ -1705,7 +1706,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true, "engines": { "node": ">= 10" } @@ -1871,6 +1871,376 @@ "node": ">=8.0.0" } }, + "node_modules/d3": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.2.tgz", + "integrity": "sha512-WXty7qOGSHb7HR7CfOzwN1Gw04MUOzN8qh9ZUsvwycIMb4DYMpY9xczZ6jUorGtO6bR9BPMPaueIKwiDxu9uiQ==", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-yEEyEAbDrF8C6Ob2myOBLjwBLck1Z89jMGFee0oPsn95GqjerpaOA4ch+vc2l0FNFFwMD5N7OCSEN5eAlsUbgQ==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.2.tgz", + "integrity": "sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", + "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==", + "dependencies": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -1957,6 +2327,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delaunator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz", + "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==", + "dependencies": { + "robust-predicates": "^3.0.0" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -3293,6 +3671,17 @@ "regenerator-runtime": "^0.13.4" } }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", @@ -3381,6 +3770,14 @@ "node": ">= 0.4" } }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -5021,6 +5418,11 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/robust-predicates": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz", + "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==" + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -5044,6 +5446,11 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -7552,8 +7959,7 @@ "commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", - "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", - "dev": true + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" }, "concat-map": { "version": "0.0.1", @@ -7675,6 +8081,269 @@ "css-tree": "^1.1.2" } }, + "d3": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.2.tgz", + "integrity": "sha512-WXty7qOGSHb7HR7CfOzwN1Gw04MUOzN8qh9ZUsvwycIMb4DYMpY9xczZ6jUorGtO6bR9BPMPaueIKwiDxu9uiQ==", + "requires": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + } + }, + "d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-yEEyEAbDrF8C6Ob2myOBLjwBLck1Z89jMGFee0oPsn95GqjerpaOA4ch+vc2l0FNFFwMD5N7OCSEN5eAlsUbgQ==", + "requires": { + "internmap": "1 - 2" + } + }, + "d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==" + }, + "d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "requires": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + } + }, + "d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "requires": { + "d3-path": "1 - 3" + } + }, + "d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==" + }, + "d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "requires": { + "d3-array": "^3.2.0" + } + }, + "d3-delaunay": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.2.tgz", + "integrity": "sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==", + "requires": { + "delaunator": "5" + } + }, + "d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==" + }, + "d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "requires": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + } + }, + "d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "requires": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + } + }, + "d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==" + }, + "d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "requires": { + "d3-dsv": "1 - 3" + } + }, + "d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "requires": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + } + }, + "d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==" + }, + "d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==", + "requires": { + "d3-array": "2.5.0 - 3" + } + }, + "d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==" + }, + "d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "requires": { + "d3-color": "1 - 3" + } + }, + "d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==" + }, + "d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==" + }, + "d3-quadtree": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==" + }, + "d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==" + }, + "d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "requires": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + } + }, + "d3-scale-chromatic": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", + "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==", + "requires": { + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + } + }, + "d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==" + }, + "d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "requires": { + "d3-path": "^3.1.0" + } + }, + "d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "requires": { + "d3-array": "2 - 3" + } + }, + "d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "requires": { + "d3-time": "1 - 3" + } + }, + "d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==" + }, + "d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "requires": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + } + }, + "d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "requires": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + } + }, "damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -7737,6 +8406,14 @@ "object-keys": "^1.1.1" } }, + "delaunator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz", + "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==", + "requires": { + "robust-predicates": "^3.0.0" + } + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -8753,6 +9430,14 @@ } } }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, "ignore": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", @@ -8820,6 +9505,11 @@ "side-channel": "^1.0.4" } }, + "internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==" + }, "is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", @@ -9998,6 +10688,11 @@ "glob": "^7.1.3" } }, + "robust-predicates": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz", + "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==" + }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -10007,6 +10702,11 @@ "queue-microtask": "^1.2.2" } }, + "rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", diff --git a/package.json b/package.json index 796663ae..8b94dc07 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "export": "NEXT_TELEMETRY_DISABLED=1 next build && NEXT_TELEMETRY_DISABLED=1 next export" }, "dependencies": { + "d3": "^7.8.2", "i18next": "^21.6.14", "i18next-browser-languagedetector": "^6.1.4", "i18next-resources-to-backend": "^1.0.0", @@ -62,4 +63,4 @@ ] } } -} \ No newline at end of file +} From a5e1e93b4f5aa2a02a3d9945592ea45cdd67fcc5 Mon Sep 17 00:00:00 2001 From: Marcel Date: Fri, 27 Jan 2023 18:22:12 +0100 Subject: [PATCH 024/714] add prototype implementation of a possible explore interface --- components/layouts/iframe.js | 2 +- pages/explore.js | 76 ------------------- pages/explore/GraphView.js | 143 +++++++++++++++++++++++++++++++++++ pages/explore/index.js | 118 +++++++++++++++++++++++++++++ 4 files changed, 262 insertions(+), 77 deletions(-) delete mode 100644 pages/explore.js create mode 100644 pages/explore/GraphView.js create mode 100644 pages/explore/index.js diff --git a/components/layouts/iframe.js b/components/layouts/iframe.js index d1cd2144..5c1704ce 100644 --- a/components/layouts/iframe.js +++ b/components/layouts/iframe.js @@ -33,7 +33,7 @@ const Sidebar = styled.div` overflow: auto; @media ${breakpoints.laptopAndAbove} { - width: 25rem; + width: ${props => props.width ? props.width : '25rem'}; padding: calc(var(--margin) * 2); } } diff --git a/pages/explore.js b/pages/explore.js deleted file mode 100644 index 63555cd0..00000000 --- a/pages/explore.js +++ /dev/null @@ -1,76 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import styled from 'styled-components'; -import getConfig from 'next/config'; -import { useTranslation } from 'react-i18next'; - -import ContextMultiLevelSelect from '../components/ContextMultiLevelSelect'; -import { useAuth } from '../lib/Auth'; - -const ExploreSection = styled.div` - & > * + * { - margin-top: var(--margin); - } - - & > select + select { - margin-top: calc(var(--margin) * 0.65); - } -`; - -export default function Explore() { - const auth = useAuth(); - const { t } = useTranslation('explore'); - - const [activeContexts, setActiveContexts] = useState([getConfig().publicRuntimeConfig.contextRootSpaceRoomId]); - const [contents, setContents] = useState(null); - - useEffect(() => { - const fetchContents = async () => { - const contents = []; - const roomHierarchy = await auth.getAuthenticationProvider('matrix').getMatrixClient().getRoomHierarchy(activeContexts[activeContexts.length - 1], undefined, 1); - // Remove the first entry, which is the context itself - roomHierarchy.rooms.shift(); - // Ignore `m.space.child` events that are empty - // We're only interested in the -contents- of this context, so filter out everything that's a sub-context - for (const room of roomHierarchy.rooms) { - const metaEvent = await auth.getAuthenticationProvider('matrix').getMatrixClient().getStateEvent(room.room_id, 'dev.medienhaus.meta').catch(() => {}); - if (!metaEvent || (metaEvent && metaEvent.type !== 'context')) { - // This is a valid content item we want to show - contents.push({ - ...room, - type: metaEvent?.type ?? 'chat', // If there's no meta.type we assume it's a chat room - template: metaEvent?.template, - }); - } - } - setContents(contents); - }; - - if (activeContexts) fetchContents(); - }, [activeContexts, auth]); - - return ( - <> -

      /explore

      - - - { (contents && contents.length > 0) ? ( -
      -
        - { contents.map((content) => ( -
      1. - { content.name } - - { content.type && (<> (type: { content.type })) } - { content.template && (<> (template: { content.template })) } - -
      2. - )) } -
      -
      - ) : ( -

      - { t('There are no contents for this context') } -

      - ) } -
      - - ); -} diff --git a/pages/explore/GraphView.js b/pages/explore/GraphView.js new file mode 100644 index 00000000..41707f70 --- /dev/null +++ b/pages/explore/GraphView.js @@ -0,0 +1,143 @@ + +import React from 'react'; +import * as d3 from 'd3'; +import { interpolateRainbow, partition, quantize, scaleOrdinal, select } from 'd3'; +// import { partition } from 'd3'; +// import { format } from 'd3'; + +function GraphView({ data, callback, parsedWidth }) { + console.log(parsedWidth); + const svgRef = React.useRef(null); + const format = d3.format(',d'); + + function rectHeight(d) { + return d.x1 - d.x0 - Math.min(1, (d.x1 - d.x0) / 2); + } + + function labelVisible(d) { + return d.y1 <= width && d.y0 >= 0 && d.x1 - d.x0 > 16; + } + + const width = parsedWidth || window.innerWidth; + const height = window.innerHeight; + console.log(window); + + function iciclePartition(data) { + const root = d3 + .hierarchy(data) + .sum((d) => d.value) + .sort( + (a, b) => b.height - a.height || b.value - a.value, + ); + return partition().size([ + height, + ((root.height + 1) * width) / 3, + ])(root); + } + + const color = scaleOrdinal( + quantize(interpolateRainbow, data.children.length + 1), + ); + const root = iciclePartition(data); + let focus = root; + + const svg = select('body') + .append('svg') + // .attr('viewBox', [root.x1, 0, width, height]) + .style('font', '10px sans-serif'); + + const cell = svg + .selectAll('g') + .data(root.descendants()) + .join('g') + .attr('transform', (d) => `translate(${d.y0},${d.x0})`); + + const rect = cell + .append('rect') + .attr('width', (d) => d.y1 - d.y0 - 1) + .attr('height', (d) => rectHeight(d)) + // .attr('fill-opacity', 1) + .attr('fill', 'var(--color-background-sidebar)') + .attr('stroke', 'var(--color-foreground)') + .attr('stroke-width', '10') + .style('cursor', 'pointer') + .on('click', clicked); + + const text = cell + .append('text') + .style('user-select', 'none') + .attr('pointer-events', 'none') + .attr('x', 20) + .attr('y', 50) + .attr('font-size', '36px') + .attr('fill', 'var(--color-foreground)') + .attr('fill-opacity', (d) => +labelVisible(d)); + + text.append('tspan').text((d) => d.data.name); + + const tspan = text + .append('tspan') + .attr('fill-opacity', (d) => labelVisible(d) * 0.7) + .text((d) => ` ${format(d.value)}`); + + cell.append('title').text( + (d) => + `${d + .ancestors() + .map((d) => d.data.name) + .reverse() + .join('/')}\n${format(d.value)}`, + ); + console.log(root); + async function clicked(event, p) { + focus = focus === p ? (p = p.parent) : p; + if (!p) return; + + root.each( + (d) => + (d.target = { + x0: ((d.x0 - p.x0) / (p.x1 - p.x0)) * height, + x1: ((d.x1 - p.x0) / (p.x1 - p.x0)) * height, + y0: d.y0 - p.y0, + y1: d.y1 - p.y0, + }), + ); + + const t = cell + .transition() + .duration(750) + .attr( + 'transform', + (d) => `translate(${d.target.y0},${d.target.x0})`, + ); + + rect + .transition(t) + .attr('height', (d) => rectHeight(d.target)); + text + .transition(t) + .attr('fill-opacity', (d) => +labelVisible(d.target)); + tspan + .transition(t) + .attr( + 'fill-opacity', + (d) => labelVisible(d.target) * 0.7, + ); + + callback(p); + } + + React.useEffect(() => { + if (svgRef.current) { + svgRef.current.appendChild(svg.node()); + } + + return () => { + svgRef.current = null; + }; + }, [svg]); + + return ; +} + +export default GraphView; diff --git a/pages/explore/index.js b/pages/explore/index.js new file mode 100644 index 00000000..86b6fa62 --- /dev/null +++ b/pages/explore/index.js @@ -0,0 +1,118 @@ +import React, { useCallback, useEffect, useState } from 'react'; +// import styled from 'styled-components'; +import getConfig from 'next/config'; + +// import ContextMultiLevelSelect from '../../components/ContextMultiLevelSelect'; +// import { useAuth } from '../../lib/Auth'; +import GraphView from './GraphView'; +// import jsonData from '../../assets/hierarchy.json'; +import IframeLayout from '../../components/layouts/iframe'; +import LoadingSpinner from '../../components/UI/LoadingSpinner'; + +// const ExploreSection = styled.div` +// & > * + * { +// margin-top: var(--margin); +// } + +// & > select + select { +// margin-top: calc(var(--margin) * 0.65); +// } +// `; + +export default function Explore() { + const [activeContexts] = useState([getConfig().publicRuntimeConfig.contextRootSpaceRoomId]); + const [graphObject, setGraphObject] = useState(null); + const [selectedNode, setSelectedNode] = useState(''); + + const fetchContents = useCallback(async () => { + // const contents = []; + // const roomHierarchy = await auth.getAuthenticationProvider('matrix').getMatrixClient().getRoomHierarchy(activeContexts[activeContexts.length - 1], undefined, 1); + // // Remove the first entry, which is the context itself + // roomHierarchy.rooms.shift(); + // // Ignore `m.space.child` events that are empty + // // We're only interested in the -contents- of this context, so filter out everything that's a sub-context + // for (const room of roomHierarchy.rooms) { + // const metaEvent = await auth.getAuthenticationProvider('matrix').getMatrixClient().getStateEvent(room.room_id, 'dev.medienhaus.meta').catch(() => {}); + // if (!metaEvent || (metaEvent && metaEvent.type !== 'context')) { + // // This is a valid content item we want to show + // contents.push({ + // ...room, + // type: metaEvent?.type ?? 'chat', // If there's no meta.type we assume it's a chat room + // template: metaEvent?.template, + // children: room.children_state, + // }); + // } + // } + // console.log(contents); + + // setContents({ + // name: 'First Level', + // children: contents, + // }); + + // initial fetch of object + const object = await fetch('http://192.168.0.50:3009/api/v2/!gBzMkmAvxvlPEwlvdq:moci.space/render/d3/fulltree').catch((err) => console.error(err)); + const json = await object.json(); + console.log(json); + setGraphObject(json); + }, []); + + useEffect(() => { + if (activeContexts) fetchContents(); + }, [activeContexts, fetchContents]); + + const handleClicked = (element) => { + // element is the last node clicked on by the user + if (!element) return; + console.log(element); + setSelectedNode(element.data.url); // if selected node is not undefined iframe loads the url(type string) from selectedNode + }; + + if (!graphObject) return ; + + return ( + <> + +

      /explore

      + +
      + { selectedNode && + + + +

      bildende kunst und so

      + + + +
      +