From 1b7715d0c895fc218b1b4caa7e56c23c1e9a0ce0 Mon Sep 17 00:00:00 2001 From: sneakers-the-rat Date: Tue, 1 Aug 2023 18:22:49 -0700 Subject: [PATCH] cleaning up components, beginning to pull out slack logic --- client/src/api/slack.ts | 4 +- client/src/components/join/joinBridge.tsx | 5 +- client/src/components/join/joinChannel.tsx | 39 ++++-- client/src/components/join/joinForm.tsx | 73 ++++------- client/src/components/join/joinGroup.tsx | 60 +++++++++ client/src/components/join/joinLogin.tsx | 72 +++++++++++ client/src/components/join/joinPlatform.tsx | 120 ------------------ client/src/components/panels/joinPanel.tsx | 49 +------ client/src/components/panels/joinSlack.tsx | 7 - .../src/components/platforms/slackLogin.tsx | 55 ++++++++ client/src/types/bridge.tsx | 6 + client/src/types/channel.tsx | 5 + client/src/types/slack.tsx | 0 server/src/controllers/slack.controller.ts | 62 ++------- server/src/types/config.ts | 6 + server/src/types/slack.ts | 36 ++++++ 16 files changed, 311 insertions(+), 288 deletions(-) create mode 100644 client/src/components/join/joinGroup.tsx create mode 100644 client/src/components/join/joinLogin.tsx delete mode 100644 client/src/components/join/joinPlatform.tsx delete mode 100644 client/src/components/panels/joinSlack.tsx create mode 100644 client/src/components/platforms/slackLogin.tsx create mode 100644 client/src/types/bridge.tsx create mode 100644 client/src/types/channel.tsx create mode 100644 client/src/types/slack.tsx create mode 100644 server/src/types/config.ts create mode 100644 server/src/types/slack.ts diff --git a/client/src/api/slack.ts b/client/src/api/slack.ts index 4fe8f25..2d87f65 100644 --- a/client/src/api/slack.ts +++ b/client/src/api/slack.ts @@ -3,7 +3,7 @@ export const getSlackInstallURL = (callback: CallableFunction) => { .then(res => res.json()) .then(res => { console.log('Got slack url', res); - callback(res.data.url, res.data.state_token) + callback(res.data.url) }) } @@ -41,4 +41,4 @@ export const getBotInfo = (callback: CallableFunction) => { fetch('api/slack/info') .then(res => res.json()) .then(res => callback(res)) -} \ No newline at end of file +} diff --git a/client/src/components/join/joinBridge.tsx b/client/src/components/join/joinBridge.tsx index 1094e85..6f7b280 100644 --- a/client/src/components/join/joinBridge.tsx +++ b/client/src/components/join/joinBridge.tsx @@ -1,9 +1,6 @@ import Grid from "@mui/material/Grid"; import TextField from "@mui/material/TextField"; import Button from "@mui/material/Button"; -import Typography from "@mui/material/Typography"; -import FormControl from "@mui/material/FormControl"; -import InputLabel from "@mui/material/InputLabel"; import {setBridgeLabel} from "../../api/bridge"; import {useState} from "react"; @@ -55,4 +52,4 @@ return ( ) -} \ No newline at end of file +} diff --git a/client/src/components/join/joinChannel.tsx b/client/src/components/join/joinChannel.tsx index e45c522..8f1416c 100644 --- a/client/src/components/join/joinChannel.tsx +++ b/client/src/components/join/joinChannel.tsx @@ -5,16 +5,15 @@ import MenuItem from "@mui/material/MenuItem"; import Button from "@mui/material/Button"; import {stepCompleteType} from "./joinForm"; -import {joinSlackChannel} from "../../api/slack"; -import {useState} from "react"; +import {getSlackChannels, joinSlackChannel} from "../../api/slack"; +import {useEffect, useState} from "react"; +import {channelsType} from "../../types/channel"; +import {bridgeType} from "../../types/bridge"; export interface JoinChannelProps { - channels: Array<{ - name: string; - id: string; - is_member: boolean; - }>; + platform: string; + bridge: bridgeType; selectedChannel: string; setSelectedChannel: CallableFunction; setStepComplete: CallableFunction; @@ -22,16 +21,26 @@ export interface JoinChannelProps { } const JoinChannel = ({ - channels, + platform, + bridge, selectedChannel, setSelectedChannel, setStepComplete, stepComplete }: JoinChannelProps) => { - console.log('joinchannel channels', channels) const [errored, setErrored] = useState(false); + const [channels, setChannels] = useState(); + useEffect(() => { + if (bridge){ + switch(platform){ + case "Slack": + getSlackChannels(setChannels) + } + } + }, [platform, bridge]) + const onJoinChannel = (response) => { if (response.status === 'success'){ setErrored(false) @@ -47,9 +56,13 @@ const JoinChannel = ({ } const onJoinButtonClicked = () => { - let channel_id = channels.filter(chan => chan.name === selectedChannel) - .map(chan => chan.id)[0] - joinSlackChannel(channel_id, onJoinChannel) + switch(platform){ + case "Slack": + let channel_id = channels.filter(chan => chan.name === selectedChannel) + .map(chan => chan.id)[0] + joinSlackChannel(channel_id, onJoinChannel) + } + } return ( @@ -95,4 +108,4 @@ const JoinChannel = ({ ) } -export default JoinChannel \ No newline at end of file +export default JoinChannel diff --git a/client/src/components/join/joinForm.tsx b/client/src/components/join/joinForm.tsx index 53f4eee..15aa11d 100644 --- a/client/src/components/join/joinForm.tsx +++ b/client/src/components/join/joinForm.tsx @@ -1,29 +1,16 @@ -import Typography from "@mui/material/Typography"; -import Accordion from '@mui/material/Accordion'; -import AccordionDetails from '@mui/material/AccordionDetails'; -import AccordionSummary from '@mui/material/AccordionSummary'; - import {Group} from "../../types/group"; import {JoinStep} from './joinStep'; -import {JoinPlatform} from "./joinPlatform"; +import {JoinLogin} from "./joinLogin"; import {useState, useEffect} from "react"; -import Grid from '@mui/material/Grid'; -import TextField from "@mui/material/TextField"; import Button from '@mui/material/Button'; -import {setBridgeLabel} from "../../api/bridge"; -import {getSlackChannels} from "../../api/slack"; import {createChannel} from "../../api/channel"; -import FormControl from "@mui/material/FormControl"; -import Select from "@mui/material/Select" -import InputLabel from '@mui/material/InputLabel'; -import MenuItem from '@mui/material/MenuItem'; import {JoinBridge} from "./joinBridge"; import JoinChannel from "./joinChannel"; +import {bridgeType} from "../../types/bridge"; export interface JoinFormProps { group?: Group - invite_token: string } export interface stepCompleteType { @@ -32,31 +19,30 @@ export interface stepCompleteType { channel: boolean; } -export const JoinForm = ({group, invite_token}: JoinFormProps) => { - const [bridgeCreated, setBridgeCreated] = useState(false); - const [bridgeErrorMessage, setBridgeErrorMessage] = useState(''); +export interface stepErrorType { + login: string; + bridge: string; + channel: string; +} + +export const JoinForm = ({group}: JoinFormProps) => { + // The platform selected to log in to const [platform, setPlatform] = useState(); - const [channels, setChannels] = useState - >(); + const [bridge, setBridge] = useState(); const [selectedChannel, setSelectedChannel] = useState(''); - const [bridge, setBridge] = useState<{ - Label: string; - Protocol: string; - team_name: string; - id: string; - }>(); + const [stepComplete, setStepComplete] = useState({ login: false, bridge: false, channel: false }); + const [bridgeCreated, setBridgeCreated] = useState(false); + const [bridgeErrorMessage, setBridgeErrorMessage] = useState(''); + + const createBridgedChannel = () => { - createChannel(selectedChannel, bridge.id, invite_token, onBridgedChannelCreated) + createChannel(selectedChannel, bridge.id, group.invite_token, onBridgedChannelCreated) } const onBridgedChannelCreated = (result) => { @@ -69,15 +55,6 @@ export const JoinForm = ({group, invite_token}: JoinFormProps) => { } } - useEffect(() => { - if (bridge !== undefined){ - setStepComplete({...stepComplete, login:true}) - } - if (bridge !== undefined && channels === undefined){ - getSlackChannels(setChannels) - } - }, [bridge]) - return ( <>
@@ -89,10 +66,13 @@ export const JoinForm = ({group, invite_token}: JoinFormProps) => { id={'login'} completed={stepComplete.login} > - { completed={stepComplete.channel} > { ) -} \ No newline at end of file +} diff --git a/client/src/components/join/joinGroup.tsx b/client/src/components/join/joinGroup.tsx new file mode 100644 index 0000000..334e5c0 --- /dev/null +++ b/client/src/components/join/joinGroup.tsx @@ -0,0 +1,60 @@ +import React, {useState} from 'react'; + +import {Group} from '../../types/group' +import {groupInvite} from '../../api/groups' +import TextField from "@mui/material/TextField"; +import Button from "@mui/material/Button"; + +export interface JoinGroupProps { + group: Group; + setGroup: React.Dispatch> +} + +export const JoinGroup = ({ + group, + setGroup +}: JoinGroupProps) => { + const [text, setText] = useState(''); + const [authError, setAuthError] = useState(false); + const [errorText, setErrorText] = useState(''); + + const onGroupLogin = (response) => { + if (response.status !== "success"){ + setAuthError(true); + setErrorText(response.message); + setGroup(undefined); + } else if (response.status === "success"){ + setAuthError(false); + setErrorText(''); + setGroup(response.data) + console.log(response) + } + } + + const handleClick = () => { + groupInvite(text, onGroupLogin) + } + + const textChanged = (event: React.ChangeEvent) => { + setText(event.target.value) + setGroup(undefined) + } + return ( +
+ + +
+ ) +} diff --git a/client/src/components/join/joinLogin.tsx b/client/src/components/join/joinLogin.tsx new file mode 100644 index 0000000..5ce9de4 --- /dev/null +++ b/client/src/components/join/joinLogin.tsx @@ -0,0 +1,72 @@ +/* +Select which platform you're joining from! + */ + +import React, {useEffect, useState} from 'react'; +import InputLabel from '@mui/material/InputLabel'; +import MenuItem from '@mui/material/MenuItem'; +import FormControl from '@mui/material/FormControl'; +import Select from '@mui/material/Select'; + +import {SlackLogin} from "../platforms/slackLogin"; + +enum PLATFORMS { + Slack = 'Slack' +} + +export const JoinLogin = ({ + platform, + setPlatform, + bridge, + setBridge, + stepComplete, + setStepComplete + }) => { + const [loginComponent, setLoginComponent] = useState(<>); + + + const handleSelect = (event) => { + setBridge(undefined); + setPlatform(event.target.value); + } + + useEffect(() => { + switch(platform){ + case "Slack": + setLoginComponent() + default: + setLoginComponent(<>) + + } + }, [platform]) + + useEffect(() => { + if (bridge !== undefined) { + setStepComplete({...stepComplete, login:true}) + } + },[bridge]) + + + return( +
+ + Select Platform + + + { + platform ? loginComponent :
+ } + +
+ ) +} diff --git a/client/src/components/join/joinPlatform.tsx b/client/src/components/join/joinPlatform.tsx deleted file mode 100644 index 742b899..0000000 --- a/client/src/components/join/joinPlatform.tsx +++ /dev/null @@ -1,120 +0,0 @@ -/* -Select which platform you're joining from! - */ - -import React, {useEffect, useRef, useState} from 'react'; -import Box from '@mui/material/Box'; -import InputLabel from '@mui/material/InputLabel'; -import MenuItem from '@mui/material/MenuItem'; -import FormControl from '@mui/material/FormControl'; -import Select from '@mui/material/Select'; -import Button from "@mui/material/Button"; - -import {getSlackInstallURL} from "../../api/slack"; -import {getBridgeByStateToken} from "../../api/bridge"; -import {JoinSlack} from "../panels/joinSlack"; - -enum PLATFORMS { - Slack = 'Slack' -} - -export const JoinPlatform = ({ - platformSetter, - bridgeSetter, - completeSetter - }) => { - const [platform, setPlatform] = useState(); - const [installLink, setInstallLink] = useState(); - const [stateToken, setStateToken] = useState(); - const [bridge, setBridge] = useState(); - - const pingTimeout = useRef(null); - - const handleSelect = (event) => { - setPlatform(event.target.value); - platformSetter(event.target.value); - } - - useEffect(() => { - if (platform === "Slack") { - console.log('Getting slack URL') - getSlackInstallURL(handleInstallLink) - } - }, [platform]) - - const handleInstallLink = (url, state_token) => { - setInstallLink(url); - setStateToken(state_token); - // pingForBridge() - } - - - useEffect(() => { - const pingForBridge = () => { - if (bridge === undefined) { - console.log('bridge is', bridge); - getBridgeByStateToken(setBridge); - - pingTimeout.current = setTimeout(pingForBridge, 1000); - } - } - if (stateToken !== undefined) { - if (bridge === undefined){ - pingForBridge() - // pingTimeout.current = setInterval(pingForBridge, 1000) - } - - } - return () => {clearInterval(pingTimeout.current)} - - // if (stateToken !== undefined){ - // if (bridge === undefined){ - // console.log('bridge is', bridge) - // pingTimeout.current = setTimeout(() => { - // getBridgeByStateToken(setBridge); - // }, 1000) - // // setTimeout(pingForBridge, 1000); - // } - // } - - }, [stateToken, bridge]) - - useEffect(() => { - bridgeSetter(bridge) - }, [bridge]) - - // useEffect(() => { - // - // }, [installLink]) - - const openInstallTab = () => { - - window.open(installLink, '_blank').focus(); - } - - return( -
- - Select Platform - - - { - installLink && platform == "Slack" ? - - :
- } - -
- ) -} \ No newline at end of file diff --git a/client/src/components/panels/joinPanel.tsx b/client/src/components/panels/joinPanel.tsx index 593c615..36f55aa 100644 --- a/client/src/components/panels/joinPanel.tsx +++ b/client/src/components/panels/joinPanel.tsx @@ -4,60 +4,25 @@ import TextField from '@mui/material/TextField'; import Button from '@mui/material/Button'; import {JoinForm} from "../join/joinForm"; +import {JoinGroup} from '../join/joinGroup'; + import {Group} from "../../types/group"; import {groupInvite} from "../../api/groups"; export default function JoinPanel(){ - const [text, setText] = useState(''); - const [authError, setAuthError] = useState(false); - const [errorText, setErrorText] = useState(''); const [group, setGroup] = useState(undefined); - const onGroupLogin = (response) => { - if (response.status !== "success"){ - setAuthError(true); - setErrorText(response.message); - setGroup(undefined); - } else if (response.status === "success"){ - setAuthError(false); - setErrorText(''); - setGroup(response.data) - console.log(response) - } - } - - const handleClick = () => { - groupInvite(text, onGroupLogin) - } - - const textChanged = (event: React.ChangeEvent) => { - setText(event.target.value) - setGroup(undefined) - } return(
-
- - -
+ { group ? : undefined }
) -} \ No newline at end of file +} diff --git a/client/src/components/panels/joinSlack.tsx b/client/src/components/panels/joinSlack.tsx deleted file mode 100644 index e9ba57f..0000000 --- a/client/src/components/panels/joinSlack.tsx +++ /dev/null @@ -1,7 +0,0 @@ - - -export const JoinSlack = () => { - return( - <> - ) -} \ No newline at end of file diff --git a/client/src/components/platforms/slackLogin.tsx b/client/src/components/platforms/slackLogin.tsx new file mode 100644 index 0000000..56690f4 --- /dev/null +++ b/client/src/components/platforms/slackLogin.tsx @@ -0,0 +1,55 @@ +import React, {useEffect, useRef, useState} from 'react' +import Button from "@mui/material/Button"; +import {getSlackInstallURL} from "../../api/slack"; +import {getBridgeByStateToken} from "../../api/bridge"; + +export const SlackLogin = ({ + bridge, + setBridge + }) => { + const [installLink, setInstallLink] = useState(); + const [installStarted, setInstallStarted] = useState(false) + const pingTimeout = useRef(null); + + const openInstallTab = () => { + window.open(installLink, '_blank').focus(); + setInstallStarted(true) + } + + useEffect(() => { + getSlackInstallURL(handleInstallLink) + }, []) + + const handleInstallLink = (url) => { + setInstallLink(url); + } + + useEffect(() => { + const pingForBridge = () => { + if (bridge === undefined) { + getBridgeByStateToken(setBridge); + pingTimeout.current = setTimeout(pingForBridge, 1000); + } + } + if (installStarted) { + if (bridge === undefined){ + pingForBridge() + } + + } + return () => {clearInterval(pingTimeout.current)} + + + }, [installStarted, bridge]) + + return( + + ) +} diff --git a/client/src/types/bridge.tsx b/client/src/types/bridge.tsx new file mode 100644 index 0000000..2ab0e15 --- /dev/null +++ b/client/src/types/bridge.tsx @@ -0,0 +1,6 @@ +export interface bridgeType { + Label: string; + Protocol: string; + team_name: string; + id: string; +} diff --git a/client/src/types/channel.tsx b/client/src/types/channel.tsx new file mode 100644 index 0000000..1a93e86 --- /dev/null +++ b/client/src/types/channel.tsx @@ -0,0 +1,5 @@ +export interface channelsType { + name: string; + id: string; + is_member: boolean; +} diff --git a/client/src/types/slack.tsx b/client/src/types/slack.tsx new file mode 100644 index 0000000..e69de29 diff --git a/server/src/controllers/slack.controller.ts b/server/src/controllers/slack.controller.ts index 014b3d1..7558622 100644 --- a/server/src/controllers/slack.controller.ts +++ b/server/src/controllers/slack.controller.ts @@ -1,14 +1,15 @@ import config from 'config'; -const { InstallProvider, LogLevel, FileInstallationStore } = require('@slack/oauth'); -import {NextFunction, Request, Response} from 'express'; +import {Request, Response} from 'express'; import {AppDataSource} from "../db/data-source"; import {Bridge} from "../entities/bridge.entity"; import {Group} from "../entities/group.entity"; import {JoinSlackChannelInput} from "../schemas/slack.schema"; -import { randomUUID } from "crypto"; -import {log} from "util"; -import {Join} from "typeorm"; +import {randomUUID} from "crypto"; import logger from "../logging"; +import {authTest, slackChannel } from "../types/slack"; +import { slackConfigType } from "../types/config"; + +const { InstallProvider, LogLevel, FileInstallationStore } = require('@slack/oauth'); const scopes = ['bot', 'channels:write', 'channels:write.invites', 'chat:write:bot', 'chat:write:user', 'users.profile:read']; const bridgeRepository = AppDataSource.getRepository(Bridge) @@ -16,61 +17,15 @@ const groupRepository = AppDataSource.getRepository(Group) const SLACK_COOKIE_NAME = "slack-oauth-state"; -const slackConfig = config.get<{ - client_id: string, - client_secret: string, - signing_secret: string, - state_secret: string -}>('slackConfig'); - -export interface slackChannel { - id: string; - name: string; - is_channel: boolean; - is_group: boolean; - is_im: boolean; - is_mpim: boolean; - is_private: boolean; - created: number; - is_archived: boolean; - is_general: boolean; - unlinked: number; - name_normalized: string; - is_shared: boolean; - is_org_shared: boolean; - is_pending_ext_shared: boolean; - pending_shared: []; - context_team_id: string; - updated: number; - creator: string; - is_ext_shared: boolean; - shared_team_ids: string[]; - is_member: boolean; - num_members: number; -} - -export interface authTest { - ok: boolean; - url: string; - team: string; - user: string; - team_id: string; - user_id: string; - bot_id: string; - is_enterprise_install: boolean; -} +const slackConfig = config.get('slackConfig'); const installer = new InstallProvider({ clientId: slackConfig.client_id, clientSecret: slackConfig.client_secret, authVersion: 'v1', scopes, - // stateSecret: slackConfig.state_secret, stateVerification: false, - // installationStore: new FileInstallationStore(), logLevel: LogLevel.DEBUG, - // stateCookieName: SLACK_COOKIE_NAME - }) @@ -94,8 +49,7 @@ export const SlackInstallLinkHandler = async( res.status(200).json({ status: 'success', data: { - url, - state_token + url } }) diff --git a/server/src/types/config.ts b/server/src/types/config.ts new file mode 100644 index 0000000..02545b9 --- /dev/null +++ b/server/src/types/config.ts @@ -0,0 +1,6 @@ +export interface slackConfigType { + client_id: string, + client_secret: string, + signing_secret: string, + state_secret: string +} diff --git a/server/src/types/slack.ts b/server/src/types/slack.ts new file mode 100644 index 0000000..b9125e3 --- /dev/null +++ b/server/src/types/slack.ts @@ -0,0 +1,36 @@ +export interface slackChannel { + id: string; + name: string; + is_channel: boolean; + is_group: boolean; + is_im: boolean; + is_mpim: boolean; + is_private: boolean; + created: number; + is_archived: boolean; + is_general: boolean; + unlinked: number; + name_normalized: string; + is_shared: boolean; + is_org_shared: boolean; + is_pending_ext_shared: boolean; + pending_shared: []; + context_team_id: string; + updated: number; + creator: string; + is_ext_shared: boolean; + shared_team_ids: string[]; + is_member: boolean; + num_members: number; +} + +export interface authTest { + ok: boolean; + url: string; + team: string; + user: string; + team_id: string; + user_id: string; + bot_id: string; + is_enterprise_install: boolean; +}