adding ExpandableListItem.tsx

This commit is contained in:
Myzel394 2022-11-13 19:51:22 +01:00
parent 1cb33a9624
commit cdea167467
12 changed files with 189 additions and 147 deletions

View File

@ -231,6 +231,11 @@
"isStored": "Auf Server gespeichert", "isStored": "Auf Server gespeichert",
"isProxying": "Wird weitergeleitet" "isProxying": "Wird weitergeleitet"
} }
},
"expandedUrls": {
"text_zero": "Keine gekürzten URLs gefunden",
"text_one": "Eine URL entkürzt",
"text_other": "{{count}} URLs entkürzt"
} }
} }
} }

View File

@ -231,6 +231,11 @@
"isStored": "Stored on Server", "isStored": "Stored on Server",
"isProxying": "Being forwarded" "isProxying": "Being forwarded"
} }
},
"expandedUrls": {
"text_zero": "No shortened URLs found",
"text_one": "Expanded 1 URL",
"text_other": "Expanded {{count}} URLs"
} }
} }
} }

View File

@ -0,0 +1,38 @@
import {ReactElement, ReactNode, useState} from "react"
import {Box, Collapse, ListItemButton, ListItemIcon, ListItemText, useTheme} from "@mui/material"
export interface ExpandedUrlsListItemProps<T> {
data: T[]
icon: ReactElement
title: string
children: ReactNode
}
export default function ExpandedUrlsListItem<T>({
data,
children,
icon,
title,
}: ExpandedUrlsListItemProps<T>): ReactElement {
const [isExpanded, setIsExpanded] = useState<boolean>(false)
const theme = useTheme()
return (
<>
<ListItemButton
onClick={() => {
if (data.length > 0) {
setIsExpanded(value => !value)
}
}}
>
<ListItemIcon>{icon}</ListItemIcon>
<ListItemText>{title}</ListItemText>
</ListItemButton>
<Collapse in={isExpanded}>
<Box bgcolor={theme.palette.background.default}>{children}</Box>
</Collapse>
</>
)
}

View File

@ -22,8 +22,6 @@ export default function LanguageButton(): ReactElement {
const {isLocked, showDialog} = useContext(LockNavigationContext) const {isLocked, showDialog} = useContext(LockNavigationContext)
const {i18n} = useTranslation() const {i18n} = useTranslation()
console.log(i18n.resolvedLanguage, SORTED_ENTRIES)
return ( return (
<Select <Select
value={i18n.resolvedLanguage} value={i18n.resolvedLanguage}

View File

@ -38,5 +38,7 @@ export * from "./NoSearchResults"
export {default as NoSearchResults} from "./NoSearchResults" export {default as NoSearchResults} from "./NoSearchResults"
export * from "./LanguageButton" export * from "./LanguageButton"
export {default as LanguageButton} from "./LanguageButton" export {default as LanguageButton} from "./LanguageButton"
export * from "./ExpandableListItem"
export {default as ExpandableListItem} from "./ExpandableListItem"
export * as SimplePageBuilder from "./simple-page-builder" export * as SimplePageBuilder from "./simple-page-builder"

View File

@ -2,13 +2,7 @@ import {ReactElement} from "react"
import {MdContentCopy} from "react-icons/md" import {MdContentCopy} from "react-icons/md"
import {Link as RouterLink} from "react-router-dom" import {Link as RouterLink} from "react-router-dom"
import { import {ListItemButton, ListItemIcon, ListItemSecondaryAction, ListItemText} from "@mui/material"
ListItemButton,
ListItemIcon,
ListItemSecondaryAction,
ListItemText,
useTheme,
} from "@mui/material"
import {AliasTypeIndicator} from "~/components" import {AliasTypeIndicator} from "~/components"
import {AliasList} from "~/server-types" import {AliasList} from "~/server-types"
@ -21,8 +15,6 @@ export interface AliasesListItemProps {
const getAddress = (alias: AliasList): string => `${alias.local}@${alias.domain}` const getAddress = (alias: AliasList): string => `${alias.local}@${alias.domain}`
export default function AliasesListItem({alias, onCopy}: AliasesListItemProps): ReactElement { export default function AliasesListItem({alias, onCopy}: AliasesListItemProps): ReactElement {
const theme = useTheme()
const isInCopyAddressMode = onCopy !== undefined const isInCopyAddressMode = onCopy !== undefined
const address = getAddress(alias) const address = getAddress(alias)

View File

@ -0,0 +1,43 @@
import {ReactElement} from "react"
import {useTranslation} from "react-i18next"
import {BsArrowsAngleExpand} from "react-icons/bs"
import {List, ListItemButton, ListItemText} from "@mui/material"
import {DecryptedReportContent} from "~/server-types"
import {ExpandableListItem} from "~/components"
export interface ExpandedUrlsListItemProps {
urls: DecryptedReportContent["messageDetails"]["content"]["expandedUrls"]
}
export default function ExpandedUrlsListItem({urls}: ExpandedUrlsListItemProps): ReactElement {
const {t} = useTranslation()
return (
<ExpandableListItem
data={urls}
icon={<BsArrowsAngleExpand />}
title={t("routes.ReportDetailRoute.sections.trackers.results.expandedUrls.text", {
count: urls.length,
})}
>
<List>
{urls.map(urlData => (
<ListItemButton
key={urlData.originalUrl}
component="a"
href={urlData.expandedUrl}
target="_blank"
rel="noopener noreferrer nofollow"
>
<ListItemText
primary={urlData.expandedUrl}
secondary={urlData.originalUrl}
/>
</ListItemButton>
))}
</List>
</ExpandableListItem>
)
}

View File

@ -1,24 +1,16 @@
import {BsImage} from "react-icons/bs" import {BsImage} from "react-icons/bs"
import {ReactElement, useState} from "react" import {ReactElement} from "react"
import {MdLocationOn} from "react-icons/md" import {MdLocationOn} from "react-icons/md"
import {useLoaderData} from "react-router-dom" import {useLoaderData} from "react-router-dom"
import {useTranslation} from "react-i18next" import {useTranslation} from "react-i18next"
import addHours from "date-fns/addHours" import addHours from "date-fns/addHours"
import isBefore from "date-fns/isBefore" import isBefore from "date-fns/isBefore"
import { import {Grid, List, ListItemButton, ListItemText} from "@mui/material"
Box,
Collapse,
Grid,
List,
ListItemButton,
ListItemIcon,
ListItemText,
useTheme,
} from "@mui/material"
import {DecryptedReportContent, ServerSettings} from "~/server-types" import {DecryptedReportContent, ServerSettings} from "~/server-types"
import {isDev} from "~/constants/development" import {isDev} from "~/constants/development"
import {ExpandableListItem} from "~/components"
export interface ProxiedImagesListItemProps { export interface ProxiedImagesListItemProps {
images: DecryptedReportContent["messageDetails"]["content"]["proxiedImages"] images: DecryptedReportContent["messageDetails"]["content"]["proxiedImages"]
@ -27,82 +19,67 @@ export interface ProxiedImagesListItemProps {
export default function ProxiedImagesListItem({images}: ProxiedImagesListItemProps): ReactElement { export default function ProxiedImagesListItem({images}: ProxiedImagesListItemProps): ReactElement {
const {t} = useTranslation() const {t} = useTranslation()
const serverSettings = useLoaderData() as ServerSettings const serverSettings = useLoaderData() as ServerSettings
const theme = useTheme()
const [showProxiedImages, setShowProxiedImages] = useState<boolean>(false) console.log(images)
return ( return (
<> <ExpandableListItem
<ListItemButton data={images}
onClick={() => { icon={<BsImage />}
if (images.length > 0) { title={t("routes.ReportDetailRoute.sections.trackers.results.proxiedImages.text", {
setShowProxiedImages(value => !value) count: images.length,
} })}
}} >
> <List>
<ListItemIcon> {images.map(image => (
<BsImage /> <ListItemButton
</ListItemIcon> href={isDev ? image.url : image.serverUrl}
<ListItemText> target="_blank"
{t("routes.ReportDetailRoute.sections.trackers.results.proxiedImages.text", { key={image.imageProxyId}
count: images.length, >
})} <ListItemText
</ListItemText> primary={image.url}
</ListItemButton> secondary={
<Collapse in={showProxiedImages}> <>
<Box bgcolor={theme.palette.background.default}> <Grid
<List> display="flex"
{images.map(image => ( flexDirection="row"
<ListItemButton alignItems="center"
href={isDev ? image.url : image.serverUrl} container
target="_blank" component="span"
key={image.imageProxyId} spacing={1}
> >
<ListItemText <Grid item component="span">
primary={image.url} <MdLocationOn />
secondary={ </Grid>
<> <Grid item component="span">
<Grid {(() => {
display="flex" if (
flexDirection="row" isBefore(
alignItems="center" new Date(),
container addHours(
component="span" image.createdAt,
spacing={1} serverSettings.imageProxyLifeTime,
> ),
<Grid item component="span"> )
<MdLocationOn /> ) {
</Grid> return t(
<Grid item component="span"> "routes.ReportDetailRoute.sections.trackers.results.proxiedImages.status.isStored",
{(() => { )
if ( } else {
isBefore( return t(
new Date(), "routes.ReportDetailRoute.sections.trackers.results.proxiedImages.status.isProxying",
addHours( )
image.createdAt, }
serverSettings.imageProxyLifeTime, })()}
), </Grid>
) </Grid>
) { </>
return t( }
"routes.ReportDetailRoute.sections.trackers.results.proxiedImages.status.isStored", />
) </ListItemButton>
} else { ))}
return t( </List>
"routes.ReportDetailRoute.sections.trackers.results.proxiedImages.status.isProxying", </ExpandableListItem>
)
}
})()}
</Grid>
</Grid>
</>
}
/>
</ListItemButton>
))}
</List>
</Box>
</Collapse>
</>
) )
} }

View File

@ -1,20 +1,11 @@
import {ReactElement, useState} from "react" import {ReactElement} from "react"
import { import {List, ListItem, Typography} from "@mui/material"
Box,
Collapse,
List,
ListItem,
ListItemButton,
ListItemIcon,
ListItemText,
Typography,
useTheme,
} from "@mui/material"
import {DecryptedReportContent} from "~/server-types" import {DecryptedReportContent} from "~/server-types"
import {BsShieldShaded} from "react-icons/bs" import {BsShieldShaded} from "react-icons/bs"
import {useTranslation} from "react-i18next" import {useTranslation} from "react-i18next"
import {ExpandableListItem} from "~/components"
export interface SinglePixelImageTrackersListItemProps { export interface SinglePixelImageTrackersListItemProps {
images: DecryptedReportContent["messageDetails"]["content"]["singlePixelImages"] images: DecryptedReportContent["messageDetails"]["content"]["singlePixelImages"]
@ -24,9 +15,6 @@ export default function SinglePixelImageTrackersListItem({
images, images,
}: SinglePixelImageTrackersListItemProps): ReactElement { }: SinglePixelImageTrackersListItemProps): ReactElement {
const {t} = useTranslation() const {t} = useTranslation()
const theme = useTheme()
const [showImageTrackers, setShowImageTrackers] = useState<boolean>(false)
const imagesPerTracker = images.reduce((acc, value) => { const imagesPerTracker = images.reduce((acc, value) => {
acc[value.trackerName] = [...(acc[value.trackerName] || []), value] acc[value.trackerName] = [...(acc[value.trackerName] || []), value]
@ -35,39 +23,25 @@ export default function SinglePixelImageTrackersListItem({
}, {} as Record<string, Array<DecryptedReportContent["messageDetails"]["content"]["singlePixelImages"][0]>>) }, {} as Record<string, Array<DecryptedReportContent["messageDetails"]["content"]["singlePixelImages"][0]>>)
return ( return (
<> <ExpandableListItem
<ListItemButton data={images}
onClick={() => { icon={<BsShieldShaded />}
if (images.length > 0) { title={t("routes.ReportDetailRoute.sections.trackers.results.imageTrackers.text", {
setShowImageTrackers(value => !value) count: images.length,
} })}
}} >
> <List>
<ListItemIcon> {Object.entries(imagesPerTracker).map(([trackerName, images]) => (
<BsShieldShaded /> <>
</ListItemIcon> <Typography variant="caption" component="h3" ml={1}>
<ListItemText> {trackerName}
{t("routes.ReportDetailRoute.sections.trackers.results.imageTrackers.text", { </Typography>
count: images.length, {images.map(image => (
})} <ListItem key={image.source}>{image.source}</ListItem>
</ListItemText>
</ListItemButton>
<Collapse in={showImageTrackers}>
<Box bgcolor={theme.palette.background.default}>
<List>
{Object.entries(imagesPerTracker).map(([trackerName, images]) => (
<>
<Typography variant="caption" component="h3" ml={1}>
{trackerName}
</Typography>
{images.map(image => (
<ListItem key={image.source}>{image.source}</ListItem>
))}
</>
))} ))}
</List> </>
</Box> ))}
</Collapse> </List>
</> </ExpandableListItem>
) )
} }

View File

@ -46,7 +46,7 @@ export default function AuthenticatedRoute(): ReactElement {
justifyContent="space-between" justifyContent="space-between"
alignItems="center" alignItems="center"
> >
<Grid item xs={12} sm={3}> <Grid item xs={12} md={3}>
<Box bgcolor={theme.palette.background.paper}> <Box bgcolor={theme.palette.background.paper}>
<List component="nav"> <List component="nav">
{sections.map(key => ( {sections.map(key => (
@ -57,7 +57,7 @@ export default function AuthenticatedRoute(): ReactElement {
</List> </List>
</Box> </Box>
</Grid> </Grid>
<Grid item xs={12} sm={9} height="100%"> <Grid item xs={12} md={9} height="100%">
<Grid <Grid
container container
direction="column" direction="column"

View File

@ -11,6 +11,7 @@ import {getReport} from "~/apis"
import {DecryptReport, SimpleOverlayInformation, SimplePageBuilder} from "~/components" import {DecryptReport, SimpleOverlayInformation, SimplePageBuilder} from "~/components"
import {WithEncryptionRequired} from "~/hocs" import {WithEncryptionRequired} from "~/hocs"
import DeleteButton from "~/route-widgets/ReportDetailRoute/DeleteButton" import DeleteButton from "~/route-widgets/ReportDetailRoute/DeleteButton"
import ExpandedUrlsListItem from "~/route-widgets/ReportDetailRoute/ExpandedUrlsListItem"
import ProxiedImagesListItem from "~/route-widgets/ReportDetailRoute/ProxiedImagesListItem" import ProxiedImagesListItem from "~/route-widgets/ReportDetailRoute/ProxiedImagesListItem"
import QueryResult from "~/components/QueryResult" import QueryResult from "~/components/QueryResult"
import SinglePixelImageTrackersListItem from "~/route-widgets/ReportDetailRoute/SinglePixelImageTrackersListItem" import SinglePixelImageTrackersListItem from "~/route-widgets/ReportDetailRoute/SinglePixelImageTrackersListItem"
@ -100,6 +101,12 @@ function ReportDetailRoute(): ReactElement {
.messageDetails.content.proxiedImages .messageDetails.content.proxiedImages
} }
/> />
<ExpandedUrlsListItem
urls={
(report as DecryptedReportContent)
.messageDetails.content.expandedUrls
}
/>
</List> </List>
</SimplePageBuilder.Section>, </SimplePageBuilder.Section>,
]} ]}

View File

@ -147,7 +147,8 @@ export interface DecryptedReportContent {
trackerUrl: string trackerUrl: string
}> }>
expandedUrls: Array<{ expandedUrls: Array<{
url: string originalUrl: string
expandedUrl: string
queryTrackers: [] queryTrackers: []
}> }>
} }