Skip to content

Commit

Permalink
Merge branch 'main' into fix/ff-income-plan
Browse files Browse the repository at this point in the history
  • Loading branch information
thorkellmani authored Dec 12, 2024
2 parents c17382e + b2c44ee commit 5af6f00
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 39 deletions.
22 changes: 16 additions & 6 deletions apps/services/bff/src/app/modules/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,14 +277,24 @@ export class AuthService {
})
}

let loginAttemptData: LoginAttemptData | undefined
const loginAttemptCacheKey = this.cacheService.createSessionKeyType(
'attempt',
query.state,
)
// Get login attempt data from the cache
const loginAttemptData = await this.cacheService.get<LoginAttemptData>(
loginAttemptCacheKey,
// Do not throw an error if the key is not found
false,
)

try {
// Get login attempt from cache
loginAttemptData = await this.cacheService.get<LoginAttemptData>(
this.cacheService.createSessionKeyType('attempt', query.state),
)
if (!loginAttemptData) {
this.logger.warn(this.cacheService.createKeyError(loginAttemptCacheKey))

return this.redirectWithError(res)
}

try {
// Get tokens and user information from the authorization code
const tokenResponse = await this.idsService.getTokens({
code: query.code,
Expand Down
68 changes: 56 additions & 12 deletions apps/web/screens/Grants/Grant/GrantSidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import { useMemo } from 'react'

import { Box, Button, LinkV2, Stack, Text } from '@island.is/island-ui/core'
import {
Box,
BoxProps,
Button,
LinkV2,
Stack,
Text,
} from '@island.is/island-ui/core'
import { useLocale } from '@island.is/localization'
import { Locale } from '@island.is/shared/types'
import { isDefined } from '@island.is/shared/utils'
import { InstitutionPanel } from '@island.is/web/components'
import { Grant } from '@island.is/web/graphql/schema'
import { LinkType, useLinkResolver } from '@island.is/web/hooks'

import { m } from '../messages'
import { useLocale } from '@island.is/localization'
import { generateStatusTag } from '../utils'

interface Props {
Expand All @@ -30,6 +37,20 @@ const generateLine = (heading: string, content?: React.ReactNode) => {
)
}

const generateSidebarPanel = (
data: Array<React.ReactElement>,
background: BoxProps['background'],
) => {
if (!data) {
return undefined
}
return (
<Box background={background} padding={3} borderRadius="standard">
<Stack space={2}>{data}</Stack>
</Box>
)
}

export const GrantSidebar = ({ grant, locale }: Props) => {
const { linkResolver } = useLinkResolver()
const { formatMessage } = useLocale()
Expand Down Expand Up @@ -100,6 +121,7 @@ export const GrantSidebar = ({ grant, locale }: Props) => {
return (
<LinkV2
key={`${f.url}-${index}`}
newTab
href={f.url}
underlineVisibility="hover"
>
Expand All @@ -113,6 +135,35 @@ export const GrantSidebar = ({ grant, locale }: Props) => {
[grant.files],
)

const supportLinksPanelData = useMemo(
() =>
grant.supportLinks
?.map((link) => {
if (!link.url || !link.text || !link.id) {
return null
}
return (
<LinkV2
newTab
key={link.id}
href={link.url}
underlineVisibility="hover"
>
<Button
size="medium"
icon="link"
iconType="outline"
variant="text"
>
{link.text}
</Button>
</LinkV2>
)
})
.filter(isDefined) ?? [],
[grant.supportLinks],
)

return (
<Stack space={3}>
<InstitutionPanel
Expand All @@ -124,16 +175,9 @@ export const GrantSidebar = ({ grant, locale }: Props) => {
img={grant.fund?.parentOrganization.logo?.url}
locale={locale}
/>
{detailPanelData.length ? (
<Box background="blue100" padding={3} borderRadius="standard">
<Stack space={2}>{detailPanelData}</Stack>
</Box>
) : undefined}
{filesPanelData.length ? (
<Box background="red100" padding={3} borderRadius="standard">
<Stack space={2}>{filesPanelData}</Stack>
</Box>
) : undefined}
{generateSidebarPanel(detailPanelData, 'blue100')}
{generateSidebarPanel(filesPanelData, 'red100')}
{generateSidebarPanel(supportLinksPanelData, 'purple100')}
</Stack>
)
}
6 changes: 6 additions & 0 deletions apps/web/screens/queries/Grants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,12 @@ export const GET_GRANT_QUERY = gql`
id
title
}
supportLinks {
id
text
url
date
}
files {
...AssetFields
}
Expand Down
3 changes: 3 additions & 0 deletions libs/cms/src/lib/generated/contentfulTypes.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1853,6 +1853,9 @@ export interface IGrantFields {
/** Files */
grantFiles?: Asset[] | undefined

/** Support links */
grantSupportLinks?: ILink[] | undefined

/** Category tags */
grantCategoryTags?: IGenericTag[] | undefined

Expand Down
7 changes: 6 additions & 1 deletion libs/cms/src/lib/models/grant.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { mapDocument, SliceUnion } from '../unions/slice.union'
import { Asset, mapAsset } from './asset.model'
import { ReferenceLink, mapReferenceLink } from './referenceLink.model'
import { Fund, mapFund } from './fund.model'
import { Link, mapLink } from './link.model'

export enum GrantStatus {
CLOSED,
Expand Down Expand Up @@ -66,6 +67,9 @@ export class Grant {
@CacheField(() => [Asset], { nullable: true })
files?: Array<Asset>

@CacheField(() => [Link], { nullable: true })
supportLinks?: Array<Link>

@CacheField(() => [GenericTag], { nullable: true })
categoryTags?: Array<GenericTag>

Expand All @@ -85,7 +89,6 @@ export const mapGrant = ({ fields, sys }: IGrant): Grant => ({
applicationUrl: fields.granApplicationUrl?.fields
? mapReferenceLink(fields.granApplicationUrl)
: undefined,

specialEmphasis: fields.grantSpecialEmphasis
? mapDocument(fields.grantSpecialEmphasis, sys.id + ':special-emphasis')
: [],
Expand Down Expand Up @@ -117,6 +120,8 @@ export const mapGrant = ({ fields, sys }: IGrant): Grant => ({
: undefined,
fund: fields.grantFund ? mapFund(fields.grantFund) : undefined,
files: (fields.grantFiles ?? []).map((file) => mapAsset(file)) ?? [],
supportLinks:
(fields.grantSupportLinks ?? []).map((link) => mapLink(link)) ?? [],
categoryTags: fields.grantCategoryTags
? fields.grantCategoryTags.map((tag) => mapGenericTag(tag))
: undefined,
Expand Down
4 changes: 3 additions & 1 deletion libs/react-spa/bff/src/lib/BffPoller.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export const BffPoller = ({
const { signIn, bffUrlGenerator } = useAuth()
const userInfo = useUserInfo()
const { postMessage } = useBffBroadcaster()
const bffBaseUrl = bffUrlGenerator()

const url = useMemo(
() => bffUrlGenerator('/user', { refresh: 'true' }),
Expand Down Expand Up @@ -86,12 +87,13 @@ export const BffPoller = ({
postMessage({
type: BffBroadcastEvents.NEW_SESSION,
userInfo: newUser,
bffBaseUrl,
})

newSessionCb()
}
}
}, [newUser, error, userInfo, signIn, postMessage, newSessionCb])
}, [newUser, error, userInfo, signIn, postMessage, newSessionCb, bffBaseUrl])

return children
}
52 changes: 33 additions & 19 deletions libs/react-spa/bff/src/lib/BffProvider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,25 +43,37 @@ export const BffProvider = ({
authState === 'logging-out'
const isLoggedIn = authState === 'logged-in'
const oldLoginPath = `${applicationBasePath}/login`
const bffBaseUrl = bffUrlGenerator()

const { postMessage } = useBffBroadcaster((event) => {
if (
isLoggedIn &&
event.data.type === BffBroadcastEvents.NEW_SESSION &&
isNewUser(state.userInfo, event.data.userInfo)
) {
setSessionExpiredScreen(true)
} else if (event.data.type === BffBroadcastEvents.LOGOUT) {
// We will wait 1 seconds before we dispatch logout action.
// The reason is that IDS will not log the user out immediately.
// Note! The bff poller may have triggered logout by that time anyways.
setTimeout(() => {
dispatch({
type: ActionType.LOGGED_OUT,
})

signIn()
}, 1000)
/**
* Filter broadcast events by matching BFF base url
*
* Since the Broadcaster sends messages to all tabs/windows/iframes
* sharing the same origin (domain), we need to explicitly check if
* the message belongs to our specific BFF instance by comparing base urls.
* This prevents handling events meant for other applications/contexts
* running on the same domain.
*/
if (event.data.bffBaseUrl === bffBaseUrl) {
if (
isLoggedIn &&
event.data.type === BffBroadcastEvents.NEW_SESSION &&
isNewUser(state.userInfo, event.data.userInfo)
) {
setSessionExpiredScreen(true)
} else if (event.data.type === BffBroadcastEvents.LOGOUT) {
// We will wait 1 seconds before we dispatch logout action.
// The reason is that IDS will not log the user out immediately.
// Note! The bff poller may have triggered logout by that time anyways.
setTimeout(() => {
dispatch({
type: ActionType.LOGGED_OUT,
})

signIn()
}, 1000)
}
}
})

Expand All @@ -71,9 +83,10 @@ export const BffProvider = ({
postMessage({
type: BffBroadcastEvents.NEW_SESSION,
userInfo: state.userInfo,
bffBaseUrl,
})
}
}, [postMessage, state.userInfo, isLoggedIn])
}, [postMessage, state.userInfo, isLoggedIn, bffBaseUrl])

/**
* Builds authentication query parameters for login redirection:
Expand Down Expand Up @@ -175,12 +188,13 @@ export const BffProvider = ({
// Broadcast to all tabs/windows/iframes that the user is logging out
postMessage({
type: BffBroadcastEvents.LOGOUT,
bffBaseUrl,
})

window.location.href = bffUrlGenerator('/logout', {
sid: state.userInfo.profile.sid,
})
}, [bffUrlGenerator, postMessage, state.userInfo])
}, [bffUrlGenerator, postMessage, state.userInfo, bffBaseUrl])

const switchUser = useCallback(
(nationalId?: string) => {
Expand Down
2 changes: 2 additions & 0 deletions libs/react-spa/bff/src/lib/bff.hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,12 @@ export enum BffBroadcastEvents {
type NewSessionEvent = {
type: BffBroadcastEvents.NEW_SESSION
userInfo: BffUser
bffBaseUrl: string
}

type LogoutEvent = {
type: BffBroadcastEvents.LOGOUT
bffBaseUrl: string
}

export type BffBroadcastEvent = NewSessionEvent | LogoutEvent
Expand Down

0 comments on commit 5af6f00

Please sign in to comment.