diff --git a/src/app/hooks/useRoomStateUpdate.js b/src/app/hooks/useRoomStateUpdate.js
new file mode 100644
index 000000000..49979146d
--- /dev/null
+++ b/src/app/hooks/useRoomStateUpdate.js
@@ -0,0 +1,23 @@
+/* eslint-disable import/prefer-default-export */
+import { useEffect } from 'react';
+
+import initMatrix from '../../client/initMatrix';
+
+import { useForceUpdate } from './useForceUpdate';
+
+export function useRoomStateUpdate(roomId) {
+ const [, forceUpdate] = useForceUpdate();
+ const mx = initMatrix.matrixClient;
+
+ useEffect(() => {
+ const handleStateEvent = (event) => {
+ if (event.getRoomId() !== roomId) return;
+ forceUpdate();
+ };
+
+ mx.on('RoomState.events', handleStateEvent);
+ return () => {
+ mx.removeListener('RoomState.events', handleStateEvent);
+ };
+ }, [roomId]);
+}
diff --git a/src/app/molecules/room-integrations/RoomIntegrations.jsx b/src/app/molecules/room-integrations/RoomIntegrations.jsx
new file mode 100644
index 000000000..83b7dce51
--- /dev/null
+++ b/src/app/molecules/room-integrations/RoomIntegrations.jsx
@@ -0,0 +1,82 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import './RoomIntegrations.scss';
+
+import { getHttpUriForMxc } from 'matrix-js-sdk';
+import { blurOnBubbling } from '../../atoms/button/script';
+
+import initMatrix from '../../../client/initMatrix';
+import colorMXID from '../../../util/colorMXID';
+
+import Avatar from '../../atoms/avatar/Avatar';
+import Text from '../../atoms/text/Text';
+import { MenuHeader } from '../../atoms/context-menu/ContextMenu';
+import { useRoomStateUpdate } from '../../hooks/useRoomStateUpdate';
+
+function Bridge({
+ name, bot, avatarSrc, channel, url,
+}) {
+ return (
+
+ );
+}
+
+Bridge.defaultProps = {
+ bot: null,
+ avatarSrc: null,
+ url: null,
+};
+
+Bridge.propTypes = {
+ name: PropTypes.string.isRequired,
+ bot: PropTypes.string,
+ avatarSrc: PropTypes.string,
+ channel: PropTypes.string.isRequired,
+ url: PropTypes.string,
+};
+
+function RoomIntegrations({ roomId }) {
+ useRoomStateUpdate(roomId);
+ const mx = initMatrix.matrixClient;
+ const room = mx.getRoom(roomId);
+ const bEvents = room.currentState.getStateEvents('uk.half-shot.bridge');
+
+ return (
+
+ Bridges
+ {bEvents.map((event) => {
+ const { protocol, channel, bridgebot } = event.getContent();
+ return (
+
+ );
+ })}
+
+ );
+}
+
+RoomIntegrations.propTypes = {
+ roomId: PropTypes.string.isRequired,
+};
+
+export default RoomIntegrations;
diff --git a/src/app/molecules/room-integrations/RoomIntegrations.scss b/src/app/molecules/room-integrations/RoomIntegrations.scss
new file mode 100644
index 000000000..b5b82c534
--- /dev/null
+++ b/src/app/molecules/room-integrations/RoomIntegrations.scss
@@ -0,0 +1,28 @@
+.bridge {
+ width: 100%;
+ padding: var(--sp-extra-tight) var(--sp-normal);
+ display: flex;
+ align-items: center;
+
+ &__container {
+ display: flex;
+ }
+
+ @media (hover: hover) {
+ &:hover {
+ background-color: var(--bg-surface-hover);
+ text-decoration: none;
+ }
+ }
+ &:focus {
+ outline: none;
+ background-color: var(--bg-surface-hover);
+ }
+ &:active {
+ background-color: var(--bg-surface-active);
+ }
+
+ &__text {
+ margin-left: var(--sp-tight);
+ }
+}
diff --git a/src/app/molecules/room-permissions/RoomPermissions.jsx b/src/app/molecules/room-permissions/RoomPermissions.jsx
index da8720cdd..734fa7b21 100644
--- a/src/app/molecules/room-permissions/RoomPermissions.jsx
+++ b/src/app/molecules/room-permissions/RoomPermissions.jsx
@@ -15,7 +15,7 @@ import SettingTile from '../setting-tile/SettingTile';
import ChevronBottomIC from '../../../../public/res/ic/outlined/chevron-bottom.svg';
-import { useForceUpdate } from '../../hooks/useForceUpdate';
+import { useRoomStateUpdate } from '../../hooks/useRoomStateUpdate';
const permissionsInfo = {
users_default: {
@@ -155,23 +155,6 @@ const spacePermsGroups = {
'Settings permissions': ['state_default', 'm.room.canonical_alias', 'm.room.power_levels'],
};
-function useRoomStateUpdate(roomId) {
- const [, forceUpdate] = useForceUpdate();
- const mx = initMatrix.matrixClient;
-
- useEffect(() => {
- const handleStateEvent = (event) => {
- if (event.getRoomId() !== roomId) return;
- forceUpdate();
- };
-
- mx.on('RoomState.events', handleStateEvent);
- return () => {
- mx.removeListener('RoomState.events', handleStateEvent);
- };
- }, [roomId]);
-}
-
function RoomPermissions({ roomId }) {
useRoomStateUpdate(roomId);
const mx = initMatrix.matrixClient;
diff --git a/src/app/organisms/room/RoomSettings.jsx b/src/app/organisms/room/RoomSettings.jsx
index 632773471..21f9d53f8 100644
--- a/src/app/organisms/room/RoomSettings.jsx
+++ b/src/app/organisms/room/RoomSettings.jsx
@@ -23,6 +23,7 @@ import RoomVisibility from '../../molecules/room-visibility/RoomVisibility';
import RoomAliases from '../../molecules/room-aliases/RoomAliases';
import RoomHistoryVisibility from '../../molecules/room-history-visibility/RoomHistoryVisibility';
import RoomEncryption from '../../molecules/room-encryption/RoomEncryption';
+import RoomIntegrations from '../../molecules/room-integrations/RoomIntegrations';
import RoomPermissions from '../../molecules/room-permissions/RoomPermissions';
import RoomMembers from '../../molecules/room-members/RoomMembers';
import RoomEmojis from '../../molecules/room-emojis/RoomEmojis';
@@ -32,6 +33,7 @@ import SettingsIC from '../../../../public/res/ic/outlined/settings.svg';
import EmojiIC from '../../../../public/res/ic/outlined/emoji.svg';
import SearchIC from '../../../../public/res/ic/outlined/search.svg';
import ShieldUserIC from '../../../../public/res/ic/outlined/shield-user.svg';
+import CategoryIC from '../../../../public/res/ic/outlined/category.svg';
import LockIC from '../../../../public/res/ic/outlined/lock.svg';
import AddUserIC from '../../../../public/res/ic/outlined/add-user.svg';
import LeaveArrowIC from '../../../../public/res/ic/outlined/leave-arrow.svg';
@@ -45,6 +47,7 @@ const tabText = {
SEARCH: 'Search',
MEMBERS: 'Members',
EMOJIS: 'Emojis',
+ INTEGRATIONS: 'Integrations',
PERMISSIONS: 'Permissions',
SECURITY: 'Security',
};
@@ -65,6 +68,10 @@ const tabItems = [{
iconSrc: EmojiIC,
text: tabText.EMOJIS,
disabled: false,
+}, {
+ iconSrc: CategoryIC,
+ text: tabText.INTEGRATIONS,
+ disabled: false,
}, {
iconSrc: ShieldUserIC,
text: tabText.PERMISSIONS,
@@ -205,6 +212,7 @@ function RoomSettings({ roomId }) {
{selectedTab.text === tabText.SEARCH && }
{selectedTab.text === tabText.MEMBERS && }
{selectedTab.text === tabText.EMOJIS && }
+ {selectedTab.text === tabText.INTEGRATIONS && }
{selectedTab.text === tabText.PERMISSIONS && }
{selectedTab.text === tabText.SECURITY && }
diff --git a/src/app/organisms/room/RoomSettings.scss b/src/app/organisms/room/RoomSettings.scss
index ab7fca5c8..07a9c6db8 100644
--- a/src/app/organisms/room/RoomSettings.scss
+++ b/src/app/organisms/room/RoomSettings.scss
@@ -18,8 +18,8 @@
padding: var(--sp-ultra-tight) var(--sp-extra-tight);
border-radius: calc(var(--bo-radius) / 2);
cursor: pointer;
-
- @media (hover:hover) {
+
+ @media (hover: hover) {
&:hover {
background-color: var(--bg-surface-hover);
box-shadow: var(--bs-surface-outline);
@@ -40,7 +40,7 @@
margin: var(--sp-extra-loose);
}
}
-
+
& .tabs {
position: sticky;
top: 0;
@@ -54,7 +54,7 @@
padding: 0 var(--sp-normal);
}
}
-
+
&__cards-wrapper {
padding: 0 var(--sp-normal);
@include dir.side(padding, var(--sp-normal), var(--sp-extra-tight));
@@ -74,8 +74,9 @@
}
.room-settings .room-permissions__card,
+.room-settings .room-integrations__bridges,
.room-settings .room-search__form,
-.room-settings .room-search__result-item ,
+.room-settings .room-search__result-item,
.room-settings .room-members {
@extend .room-settings__card;
-}
\ No newline at end of file
+}