mirror of
https://github.com/Myzel394/kleckrelay-website.git
synced 2025-06-21 00:30:31 +02:00
adding ExpandableListItem.tsx
This commit is contained in:
parent
1cb33a9624
commit
cdea167467
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
38
src/components/ExpandableListItem.tsx
Normal file
38
src/components/ExpandableListItem.tsx
Normal 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>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
@ -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}
|
||||||
|
@ -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"
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
43
src/route-widgets/ReportDetailRoute/ExpandedUrlsListItem.tsx
Normal file
43
src/route-widgets/ReportDetailRoute/ExpandedUrlsListItem.tsx
Normal 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>
|
||||||
|
)
|
||||||
|
}
|
@ -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>
|
|
||||||
</>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -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"
|
||||||
|
@ -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>,
|
||||||
]}
|
]}
|
||||||
|
@ -147,7 +147,8 @@ export interface DecryptedReportContent {
|
|||||||
trackerUrl: string
|
trackerUrl: string
|
||||||
}>
|
}>
|
||||||
expandedUrls: Array<{
|
expandedUrls: Array<{
|
||||||
url: string
|
originalUrl: string
|
||||||
|
expandedUrl: string
|
||||||
queryTrackers: []
|
queryTrackers: []
|
||||||
}>
|
}>
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user