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",
"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",
"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 {i18n} = useTranslation()
console.log(i18n.resolvedLanguage, SORTED_ENTRIES)
return (
<Select
value={i18n.resolvedLanguage}

View File

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

View File

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

View File

@ -1,20 +1,11 @@
import {ReactElement, useState} from "react"
import {ReactElement} from "react"
import {
Box,
Collapse,
List,
ListItem,
ListItemButton,
ListItemIcon,
ListItemText,
Typography,
useTheme,
} from "@mui/material"
import {List, ListItem, Typography} from "@mui/material"
import {DecryptedReportContent} from "~/server-types"
import {BsShieldShaded} from "react-icons/bs"
import {useTranslation} from "react-i18next"
import {ExpandableListItem} from "~/components"
export interface SinglePixelImageTrackersListItemProps {
images: DecryptedReportContent["messageDetails"]["content"]["singlePixelImages"]
@ -24,9 +15,6 @@ export default function SinglePixelImageTrackersListItem({
images,
}: SinglePixelImageTrackersListItemProps): ReactElement {
const {t} = useTranslation()
const theme = useTheme()
const [showImageTrackers, setShowImageTrackers] = useState<boolean>(false)
const imagesPerTracker = images.reduce((acc, 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]>>)
return (
<>
<ListItemButton
onClick={() => {
if (images.length > 0) {
setShowImageTrackers(value => !value)
}
}}
>
<ListItemIcon>
<BsShieldShaded />
</ListItemIcon>
<ListItemText>
{t("routes.ReportDetailRoute.sections.trackers.results.imageTrackers.text", {
count: images.length,
})}
</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>
))}
</>
<ExpandableListItem
data={images}
icon={<BsShieldShaded />}
title={t("routes.ReportDetailRoute.sections.trackers.results.imageTrackers.text", {
count: images.length,
})}
>
<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"
alignItems="center"
>
<Grid item xs={12} sm={3}>
<Grid item xs={12} md={3}>
<Box bgcolor={theme.palette.background.paper}>
<List component="nav">
{sections.map(key => (
@ -57,7 +57,7 @@ export default function AuthenticatedRoute(): ReactElement {
</List>
</Box>
</Grid>
<Grid item xs={12} sm={9} height="100%">
<Grid item xs={12} md={9} height="100%">
<Grid
container
direction="column"

View File

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

View File

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