Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(Mattermost Plugin): Load modals correctly #10744

Merged
merged 2 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 23 additions & 6 deletions packages/mattermost-plugin/Atmosphere.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,20 @@ import RelayModernStore from 'relay-runtime/lib/store/RelayModernStore'
import {AnyAction, Store} from '@reduxjs/toolkit'
import {Client4} from 'mattermost-redux/client'
import {GlobalState} from 'mattermost-redux/types/store'
import {login as onLogin} from './reducers'
import {authToken as getAuthToken} from './selectors'
RelayFeatureFlags.ENABLE_RELAY_RESOLVERS = true

type State = {
authToken: string | null
serverUrl: string
store: Store<GlobalState, AnyAction>
}

const fetchFunction = (state: State) => (params: RequestParameters, variables: Variables) => {
const {serverUrl, authToken} = state
const fetchGraphQL = (state: State) => (params: RequestParameters, variables: Variables) => {
const {serverUrl, store} = state
const authToken = getAuthToken(store.getState())
const response = fetch(
serverUrl,
serverUrl + '/graphql',
Client4.getOptions({
method: 'POST',
headers: {
Expand Down Expand Up @@ -68,12 +70,12 @@ export class Atmosphere extends Environment {

constructor(serverUrl: string, reduxStore: Store<GlobalState, AnyAction>) {
const state = {
serverUrl: serverUrl + '/graphql',
serverUrl,
store: reduxStore,
authToken: null
}

const network = Network.create(fetchFunction(state))
const network = Network.create(fetchGraphQL(state))
const relayStore = new RelayModernStore(new RecordSource(), {
resolverContext: {
store: reduxStore,
Expand All @@ -87,6 +89,21 @@ export class Atmosphere extends Environment {
})
this.state = state
}

async login() {
const {serverUrl, store} = this.state
const response = await fetch(
serverUrl + '/login',
Client4.getOptions({
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
})
)
const body = await response.json()
store.dispatch(onLogin(body.authToken))
}
}

/**
Expand Down
31 changes: 1 addition & 30 deletions packages/mattermost-plugin/AtmosphereProvider.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,12 @@
import {Client4} from 'mattermost-redux/client'
import {ReactNode, useCallback, useEffect} from 'react'
import {useSelector} from 'react-redux'
import {ReactNode} from 'react'
import {RelayEnvironmentProvider} from 'react-relay'
import {Atmosphere} from './Atmosphere'
import {getPluginServerRoute} from './selectors'

type Props = {
environment: Atmosphere
children: ReactNode
}

export default function AtmosphereProvider({environment, children}: Props) {
const pluginServerRoute = useSelector(getPluginServerRoute)
const serverUrl = `${pluginServerRoute}/login`
const login = useCallback(async () => {
const response = await fetch(
serverUrl,
Client4.getOptions({
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
})
)
const body = await response.json()
environment.state.authToken = body.authToken
}, [serverUrl])

useEffect(() => {
if (!environment.state.authToken) {
login()
}
}, [environment.state.authToken, login])

if (!environment.state.authToken) {
return null
}

return <RelayEnvironmentProvider environment={environment}>{children}</RelayEnvironmentProvider>
}
19 changes: 19 additions & 0 deletions packages/mattermost-plugin/components/AutoLogin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {useEffect} from 'react'
import {useSelector} from 'react-redux'
import useAtmosphere from '../hooks/useAtmosphere'
import {isAuthorized} from '../selectors'

const AutoLogin = () => {
const atmosphere = useAtmosphere()
const loggedIn = useSelector(isAuthorized)

useEffect(() => {
if (!loggedIn) {
atmosphere.login()
}
}, [atmosphere, loggedIn])

return null
}

export default AutoLogin
20 changes: 18 additions & 2 deletions packages/mattermost-plugin/components/Sidepanel/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import styled from 'styled-components'

import {useSelector} from 'react-redux'
import useAtmosphere from '../../hooks/useAtmosphere'
import {getPluginServerRoute, isAuthorized} from '../../selectors'
import ActiveMeetings from './ActiveMeetings'
import LinkedTeams from './LinkedTeams'

Expand All @@ -14,11 +16,25 @@ const Panel = styled.div!`

const SidePanelRoot = () => {
const atmosphere = useAtmosphere()
const loggedIn = useSelector(isAuthorized)
const pluginServerRoute = useSelector(getPluginServerRoute)

return (
<Panel>
<LinkedTeams />
{atmosphere.state.authToken && <ActiveMeetings />}
{loggedIn ? (
<>
<LinkedTeams />
<ActiveMeetings />
</>
) : (
<div>
<p>
You are not logged in to{' '}
<a href={`${pluginServerRoute}/parabol/create-account`}>Parabol</a>
</p>
<button onClick={atmosphere.login}>Login</button>
</div>
)}
</Panel>
)
}
Expand Down
7 changes: 6 additions & 1 deletion packages/mattermost-plugin/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {PluginRegistry} from './types/mattermost-webapp'

import {createEnvironment} from './Atmosphere'
import AtmosphereProvider from './AtmosphereProvider'
import AutoLogin from './components/AutoLogin'
import ModalRoot from './components/ModalRoot'

export const init = async (registry: PluginRegistry, store: Store<GlobalState, AnyAction>) => {
Expand All @@ -26,7 +27,11 @@ export const init = async (registry: PluginRegistry, store: Store<GlobalState, A
*/

registry.registerReducer(rootReducer)

registry.registerRootComponent(() => (
<AtmosphereProvider environment={environment}>
<AutoLogin />
</AtmosphereProvider>
))
registry.registerRootComponent(() => (
<AtmosphereProvider environment={environment}>
<ModalRoot />
Expand Down
19 changes: 11 additions & 8 deletions packages/mattermost-plugin/reducers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {AnyAction, createAsyncThunk, createSlice, PayloadAction} from '@reduxjs/toolkit'
import {createAsyncThunk, createSlice, PayloadAction} from '@reduxjs/toolkit'
import {Client4} from 'mattermost-redux/client'
import {getPluginServerRoute} from './selectors'

Expand Down Expand Up @@ -51,6 +51,7 @@ export const removeTeamFromChannel = createAsyncThunk(
const localSlice = createSlice({
name: 'local',
initialState: {
authToken: null as string | null,
isStartActivityModalVisible: false,
isCreateTaskModalVisible: false,
isInviteToTeamModalVisible: false,
Expand All @@ -60,6 +61,12 @@ const localSlice = createSlice({
linkedTeamIds: {} as Record<string, {loading: boolean; teamIds: string[]}>
},
reducers: {
login: (state, action: PayloadAction<string>) => {
state.authToken = action.payload
},
logout: (state) => {
state.authToken = null
},
openStartActivityModal: (state) => {
state.isStartActivityModalVisible = true
},
Expand Down Expand Up @@ -136,6 +143,8 @@ const localSlice = createSlice({
})

export const {
login,
logout,
openStartActivityModal,
closeStartActivityModal,
openCreateTaskModal,
Expand All @@ -152,10 +161,4 @@ export const {

export type PluginState = ReturnType<typeof localSlice.reducer>

const rootReducer = (state: PluginState, action: AnyAction) => {
const localState = localSlice.reducer(state, action)
return {
...localState
}
}
export default rootReducer
export default localSlice.reducer
3 changes: 3 additions & 0 deletions packages/mattermost-plugin/selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ export const getAssetsUrl = (state: GlobalState) => {
export const getPluginState = (state: GlobalState) =>
((state as any)[`plugins-${id}`] ?? {}) as PluginState

export const authToken = (state: GlobalState) => getPluginState(state).authToken
export const isAuthorized = (state: GlobalState) => !!authToken(state)

export const isStartActivityModalVisible = (state: GlobalState) =>
getPluginState(state).isStartActivityModalVisible

Expand Down