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

Merging Accelerated Media from Dev to Main #31

Merged
merged 87 commits into from
Sep 25, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
87 commits
Select commit Hold shift + click to select a range
e69a756
feat: using getImageURL
rezakalfane Sep 18, 2023
c3c9dfa
chore: removed different quality values
rezakalfane Sep 18, 2023
4b72d9c
feat: add basic statistics gathering for images
rs-amp Sep 18, 2023
497d866
feat: sort formats
rs-amp Sep 18, 2023
12f561a
chore: default format
rezakalfane Sep 19, 2023
c4f80ec
chore: default format for images set to AVIF
rezakalfane Sep 19, 2023
1bb1c61
fix: calculate size from arraybuffer when header is not available
rs-amp Sep 19, 2023
325963e
feat: add simple summary bar graph
rs-amp Sep 19, 2023
5a402fe
feat: added accelerated media switch
rezakalfane Sep 19, 2023
8fb7f54
feat: accelerated media context
rezakalfane Sep 19, 2023
ada6bf7
feat: swtich triggering image reload
rezakalfane Sep 19, 2023
36f5dca
chore: file location change
rezakalfane Sep 19, 2023
e82ca92
fix: fix typescript complaints
rs-amp Sep 19, 2023
ac6a64e
chore: limiting width to 2000 max
rezakalfane Sep 19, 2023
3f921b6
feat: improve details view
rs-amp Sep 19, 2023
5ed7c5f
feat: add stacked bar chart for image statistics
rs-amp Sep 19, 2023
9e9b6de
chore: styles updates
rezakalfane Sep 19, 2023
86bd9d4
feat: list view and export as csv package
rezakalfane Sep 20, 2023
ac16e93
chore: complete csv data and updated thumbnails
rezakalfane Sep 20, 2023
8c08c38
chore: accelerated media enabled and fixed table
rezakalfane Sep 20, 2023
99c3a8e
chore: updated z-index for the search box
rezakalfane Sep 20, 2023
3ffd833
chore: modal style update
rezakalfane Sep 20, 2023
ecd3de4
feat: using proper buttons for actions
rezakalfane Sep 20, 2023
b27224d
feat: all buttons at the bottom of the modal
rezakalfane Sep 20, 2023
8b0972c
feat: also get image sets on the front-end
rezakalfane Sep 20, 2023
94af1ed
feat: add bar chart
rs-amp Sep 20, 2023
8f4f029
Merge branch 'feat/accelerated-media' of github.com:amplience/dc-demo…
rs-amp Sep 20, 2023
7894c42
chore: using material ui modal component
rezakalfane Sep 20, 2023
4fe7a40
chore: styling sidebar
rezakalfane Sep 20, 2023
4b32249
feat: clickable images and thumbnails
rezakalfane Sep 20, 2023
8377c57
chore: updated accelerated media icon
rezakalfane Sep 20, 2023
f650c40
feat: modal takes full space
rezakalfane Sep 20, 2023
3a7231a
chore: styling of the card
rezakalfane Sep 20, 2023
8872fe3
fix: message when no image detected, typos
rezakalfane Sep 20, 2023
be1dffe
fix: added noreferrer in link
rezakalfane Sep 20, 2023
665d476
feat: improve individual bar chart
rs-amp Sep 20, 2023
a805d78
chore: updated styling of text
rezakalfane Sep 20, 2023
e6adc33
Merge branch 'feat/accelerated-media' of github.com:amplience/dc-demo…
rs-amp Sep 20, 2023
ac657d3
fix: fix ordering of same size formats
rs-amp Sep 20, 2023
0403c99
Merge branch 'feat/accelerated-media' of github.com:amplience/dc-demo…
rs-amp Sep 20, 2023
a5d4042
chore: overflow auto style
rezakalfane Sep 20, 2023
1e87f2c
chore: updated styling for grid and list
rezakalfane Sep 20, 2023
1266cf2
feat: add option to exclude images that can't be converted to avif
rs-amp Sep 20, 2023
6129a7e
Merge branch 'feat/accelerated-media' of github.com:amplience/dc-demo…
rs-amp Sep 20, 2023
48569b1
chore: styling of checkbox label
rezakalfane Sep 20, 2023
78cac76
fix: updated params and then add extra from URL
rezakalfane Sep 20, 2023
b146a37
feat: predict accept header for better behaviour of auto format
rs-amp Sep 20, 2023
e31bfbd
feat: use colours from individual graphs in summary
rs-amp Sep 20, 2023
15d5a65
fix: removing all image parameters
rezakalfane Sep 20, 2023
41b3790
Merge branch 'feat/accelerated-media' of https://github.com/amplience…
rezakalfane Sep 20, 2023
9ec9f9d
chore: max file width 1500 pixels
rezakalfane Sep 20, 2023
cc451e1
chore: order by size
rezakalfane Sep 20, 2023
4b46546
chore: front end pics to 1500 max
rezakalfane Sep 20, 2023
2f16293
chore: added title attribute to image links
rezakalfane Sep 20, 2023
5af0b35
chore: nothing to be done, you can just close
rezakalfane Sep 20, 2023
d8ffd0e
fix: text wrap on bar chart
rs-amp Sep 21, 2023
f1d5edb
fix: rebuild URL from transformations and existing
rezakalfane Sep 21, 2023
e7ac568
Merge branch 'feat/accelerated-media' of https://github.com/amplience…
rezakalfane Sep 21, 2023
367a8fb
chore: updated width
rezakalfane Sep 21, 2023
a9809ed
chore: updated templates
rezakalfane Sep 21, 2023
37d0ee8
chore: removed default width
rezakalfane Sep 21, 2023
b0e527e
chore: updated sizes for cache reset
rezakalfane Sep 21, 2023
e88df88
feat: add limiter for image size
rs-amp Sep 21, 2023
c27e7f0
Merge branch 'feat/accelerated-media' of github.com:amplience/dc-demo…
rs-amp Sep 21, 2023
8e095e5
fix: passing format to images
rezakalfane Sep 21, 2023
964baee
fix: updated to default quality
rezakalfane Sep 21, 2023
66a999a
feat: using accelerated media for the blog page
rezakalfane Sep 21, 2023
31de45d
fix: using getImageURL to get correct format
rezakalfane Sep 21, 2023
a79aaac
fix: using next link for internal links
rezakalfane Sep 21, 2023
4352270
feat: avoid page reload when navigating to blog page
rs-amp Sep 21, 2023
4791bcb
feat: allow overflow for bar chart values (prefer showing them)
rs-amp Sep 21, 2023
0d4e898
fix: set max sizes when no value provided
rezakalfane Sep 21, 2023
0f85349
chore: accelerated media documentation
rezakalfane Sep 21, 2023
7fa15d2
chore: updated documentation
rezakalfane Sep 22, 2023
7e0f25d
fix: removed fix width, adding max h and w
rezakalfane Sep 22, 2023
6890b1c
chore: added note about getting stats
rezakalfane Sep 22, 2023
6654988
feat: clear image statistics when changing page
rs-amp Sep 22, 2023
7a03076
chore: image statistics reset
rezakalfane Sep 22, 2023
14f2939
fix: fix unconstrained image size, old refs to i1.adis.ws
rs-amp Sep 22, 2023
38092c2
fix: getting set name from amplience set
rezakalfane Sep 25, 2023
debcbbc
fix: removed old domains logic
rezakalfane Sep 25, 2023
c733ec7
fix: using internal links for breadcrumbs
rezakalfane Sep 25, 2023
28c6b07
chore: added content about invalid images
rezakalfane Sep 25, 2023
fb2764d
Merge pull request #30 from amplience/main
rezakalfane Sep 25, 2023
b3287ae
Merge pull request #29 from amplience/feat/accelerated-media
rezakalfane Sep 25, 2023
dce93c2
fix: using set name from URL
rezakalfane Sep 25, 2023
a3adcf9
chore: version upgrade to 2.1.0
rezakalfane Sep 25, 2023
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ In your Vercel project browse to Settings --> Environment Variables and edit the


## Additional Topics
- [Features Highlights](docs/FeatureHiLites.md)
- [Features Highlights](docs/FeatureHighlights.md)
- [High-Level Architecture](docs/ArchDiagram.md)
- [Available Components](docs/Components.md)
- [Exploring features](docs/DeepDive.md)
Expand Down
24 changes: 21 additions & 3 deletions components/admin/AdminPanel/AdminPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,28 @@ import VisibilityIcon from '@mui/icons-material/Visibility';
import Accordion from '@mui/material/Accordion';
import AccordionSummary from '@mui/material/AccordionSummary';
import AccordionDetails from '@mui/material/AccordionDetails';
import ElectricBoltIcon from '@mui/icons-material/ElectricBolt';
import { withStyles, WithStyles } from '@mui/styles'

import WithAdminTheme from '@components/admin/AdminTheme';
import ComponentsPanel from './panels/ComponentsPanel';
import ContentPreviewPanel from './panels/ContentPreviewPanel';
import { getHubName } from '@lib/config/locator/config-locator';
import { useECommerce } from '@components/core/Masthead/ECommerceContext';
import AcceleratedMediaPanel from './panels/AcceleratedMediaPanel';

const styles = (theme: Theme) => ({
root: {
},
logo: {
display: 'flex',
padding: '10px 10px 4px 10px',
justifyContent: 'left'
justifyContent: 'center'
},
environment: {
display: 'flex',
justifyContent: 'left',
padding: '8px'
},
icon: {
marginRight: '0.4rem',
Expand Down Expand Up @@ -50,11 +57,12 @@ const AdminPanel: React.FunctionComponent<Props> = (props) => {
<Image src="/images/amplience.png" width={247} height={100} alt='amplience' />
</div>
<Divider />
<div className={classes.logo}>
<div className={classes.environment}>
<div>
<span>hub</span> <span><b>{hubname}</b></span>
</div>
<div style={{ marginLeft: '40px' }}>
<div style={{flexGrow: 1}} />
<div style={{justifyContent: 'right'}}>
<span>vendor</span> <span><b>{vendor}</b></span>
</div>
</div>
Expand All @@ -78,6 +86,16 @@ const AdminPanel: React.FunctionComponent<Props> = (props) => {
<ComponentsPanel />
</AccordionDetails>
</Accordion>

<Accordion key={'Accelerated Media'}>
<AccordionSummary expandIcon={<ExpandMoreIcon />} aria-controls="panel1a-content">
<ElectricBoltIcon className={classes.icon} />
<Typography variant="button">{'Accelerated Media'}</Typography>
</AccordionSummary>
<AccordionDetails>
<AcceleratedMediaPanel />
</AccordionDetails>
</Accordion>
</div>
</WithAdminTheme>
);
Expand Down
126 changes: 126 additions & 0 deletions components/admin/AdminPanel/ImageStatistics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
export interface ImageStatistics {
src: string;
name: string;
types: { [key: string]: string }
sizes: { [key: string]: number }
auto: string;
completed: number,
total: number
}

const formatTests = ['auto', 'jpeg', 'webp', 'avif']; // png deliberately excluded

export const formatColors: { [key: string]: string } = {
jpeg: '#FFA200',
webp: '#00B6FF',
avif: '#65CC02',
auto: '#8F9496',
png: '#E94420'
}

export const typeFromFormat: { [key: string]: string } = {
'image/webp': 'webp',
'image/jpeg': 'jpeg',
'image/avif': 'avif',
'image/png': 'png'
};


export function isValid(stat: ImageStatistics, key: string): boolean {
let type = stat.types[key];
let realKey = typeFromFormat[type] ?? key;

return key === 'auto' || key === realKey;
}

export function hasInvalid(stat: ImageStatistics): boolean {
for (const key of Object.keys(stat.sizes)) {
if (!isValid(stat, key)) {
return true;
}
}

return false;
}

function getAcceptHeader(): string {
// TODO: guess accept header based on browser version?
return 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8';
}

export async function DetermineImageSizes(onChange: (stats: ImageStatistics[]) => void) {
const images = Array.from(document.images);

const uniqueSrc = new Set<string>();
const result: ImageStatistics[] = [];

const promises: Promise<any>[] = [];

for (const image of images) {
const src = image.currentSrc;

if (uniqueSrc.has(src)) {
continue;
}

uniqueSrc.add(src);

try {
const url = new URL(src);

const isAmplienceRequest = url.pathname.startsWith('/i/') || url.pathname.startsWith('/s/');
const accountName = url.pathname.split('/')[2];

if (isAmplienceRequest) {
const imageResult: ImageStatistics = {
src,
name: url.pathname.split('/')[3],
types: {},
sizes: {},
completed: 0,
auto: 'none',
total: formatTests.length
}

result.push(imageResult);

onChange(result);

const formatPromises = formatTests.map(async format => {
url.searchParams.set('fmt', format);

const src = url.toString();

try {
const response = await fetch(src, { headers: { Accept: getAcceptHeader() }});

const headLength = response.headers.get("content-length");
const size = headLength ? Number(headLength) : (await response.arrayBuffer()).byteLength;

imageResult.sizes[format] = size;
imageResult.types[format] = response.headers.get("content-type") ?? '';
imageResult.completed++;

if (format === 'auto') {
imageResult.auto = typeFromFormat[imageResult.types[format]] ?? 'none'
}

onChange(result);
} catch (e) {
console.log(`Could not scan image ${image.currentSrc}`);
}
});

promises.push(...formatPromises);
}
} catch (e) {
console.log(`Not a valid URL ${image.currentSrc}`);
}
}

onChange(result);

await Promise.all(promises);

return result;
}
103 changes: 103 additions & 0 deletions components/admin/AdminPanel/ImageStatisticsBars.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import React, { FC } from 'react'
import { ImageStatistics, typeFromFormat, formatColors } from './ImageStatistics';
import { Theme, Tooltip } from '@mui/material';
import { WithStyles, withStyles } from '@mui/styles';

const styles = (theme: Theme) => ({
container: {
width: '100%',
display: 'flex',
flexDirection: 'column' as 'column'
},
barBase: {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
color: '#444444',
height: '20px',
margin: '2px 0',
fontSize: '12px',
gap: '5px'
},
format: {
fontSize: '12px',
marginLeft: '4px',
whiteSpace: 'nowrap' as 'nowrap'
},
size: {
fontSize: '12px',
marginRight: '4px',
}
});

interface Props extends WithStyles<typeof styles> {
stat: ImageStatistics;
}

interface OrderedFormat {
key: string,
size: number,
auto: boolean,
realKey: string | null
}

function getRealType(stat: ImageStatistics, key: string): string | null {
let type = stat.types[key];

const realKey = typeFromFormat[type] ?? key;

return key === 'auto' || realKey == key ? null : realKey;
}

function getOrderedFormats(stat: ImageStatistics): OrderedFormat[] {
// Formats ordered by size.
const formatSizes = Object.keys(stat.sizes)
.sort()
.filter(key => key !== 'auto')
.map(key => ({
key,
size: stat.sizes[key],
same: [key],
auto: key === stat.auto,
realKey: getRealType(stat, key)
}));

formatSizes.sort((a, b) => a.size - b.size);

return formatSizes;
}

const ImageStatisticsBars: FC<Props> = ({stat, classes}) => {
const ordered = getOrderedFormats(stat);
const maxSize = ordered[ordered.length - 1].size;
const maxKey = ordered[ordered.length - 1].key;
// ordered.reverse();

return <div className={classes.container}>
{
ordered.map((elem, index) => {
const size = elem.size;
const name = elem.key;
const invalid = elem.realKey != null;
const titleName = invalid ? `"${name}" (got ${elem.realKey})` : name;
const title = `${titleName}: ${elem.size} bytes (${Math.round(1000 * elem.size / maxSize) / 10}% of ${maxKey})`;

return <Tooltip key={elem.key} title={title}>
<div className={classes.barBase} style={{
backgroundColor: formatColors[invalid ? 'auto' : elem.key],
width: `${(size / maxSize) * 100}%`,
outline: invalid ? '1px solid red' : ''
}}>
<span>
<span className={classes.format} style={{textDecoration: invalid ? 'line-through' : ''}}>{`${name}${elem.auto ? ' (auto)' : ''}`}</span>
{invalid ? <span className={classes.format}>{elem.realKey}</span> : null}
</span>
<span className={classes.size}>{elem.size}</span>
</div>
</Tooltip>
})
}
</div>
}

export default withStyles(styles)(ImageStatisticsBars);
Loading