@proca/widget
Version:
Proca is an open-source campaign toolkit designed to empower activists and organisations in their digital advocacy efforts. It provides a flexible and customisable platform for creating and managing online petitions, email campaigns, and other forms of di
215 lines (198 loc) • 5.59 kB
JavaScript
import React, { useState, useEffect, useMemo } from "react";
import { useSupabase } from "@lib/supabase";
import Dialog from "@components/Dialog";
import { TextField, MenuItem, Grid } from "@material-ui/core";
import { useCampaignConfig } from "@hooks/useConfig";
import { makeStyles } from "@material-ui/core/styles";
import { thumbHashToDataURL } from "thumbhash";
import { base64ToBinary } from "@lib/hash";
const useStyles = makeStyles(() => ({
bimg: {
width: "100%",
objectFit: "contain",
borderRadius: 5,
},
timg: {
// animation: `$showImg 1300ms`,
width: "100%",
height: "auto",
maxWidth: "100%",
maxHeight: "100%",
borderRadius: 5,
},
img: {
// animation: `$showImg 1300ms`,
maxWidth: "100%",
maxHeight: "100%",
background: "lightgrey",
objectFit: "contain",
height: 0,
// height: "auto",
// width: "auto",
cursor: "pointer",
borderRadius: 5,
},
"@keyframes showImg": {
"0%": {
opacity: 0.4,
},
"100%": {
opacity: 1,
},
},
}));
const setBlurhash = (event, picture) => {
event.target.onError = null;
event.target.src = getBackground(picture);
// event.target.srcset = event.target.src;
};
const replaceBlur = event => {
event.target.nextElementSibling.remove();
event.target.style.height = "auto";
// event.target.srcset = event.target.src;
};
const getBackground = picture => {
if (!picture.blurhash) return null;
try {
return thumbHashToDataURL(base64ToBinary(picture.blurhash));
} catch (e) {
console.error("can't decode the blurhash", picture.blurhash, e.toString());
}
};
const usePlaceholder = (width, height) =>
useMemo(() => {
const canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext("2d");
ctx.fillStyle = "lightgrey";
ctx.fillRect(0, 0, canvas.width, canvas.height);
const dataUrl = canvas.toDataURL();
return dataUrl;
}, [width, height]);
const makeUrl = (pic, campaignName) => {
if (campaignName === "restorenaturepics") {
//TODO: remove legacy
return (
process.env.REACT_APP_SUPABASE_URL +
"/storage/v1/object/public/picture/" +
campaignName +
"/" +
pic.hash +
".jpg"
);
}
return (
process.env.REACT_APP_SUPABASE_URL +
"/storage/v1/object/public/" +
campaignName +
"/public/" +
pic.hash +
".jpg"
);
};
const PictureWall = props => {
const classes = useStyles();
const supabase = useSupabase();
const [pictures, setPictures] = useState([]);
const [selected, select] = useState(false);
const [country, setCountry] = useState(props.country);
const [countries, setCountries] = useState([]);
const config = useCampaignConfig();
const campaign = config.campaign.name; //.replaceAll("_", "-");
const placeholder = usePlaceholder(600, 800);
const handleClose = () => {
select(false);
};
useEffect(() => {
(async () => {
if (config.component.wall.country !== true) return;
let { data, error } = await supabase
.from("picture_by_country")
.select("area,total")
.order("total", { ascending: false });
if (error) {
console.error(error);
return;
}
setCountries(data);
})();
}, [config.component.wall.country, supabase]);
useEffect(() => {
(async () => {
let query = supabase
.from("pictures")
.select("hash,legend,lang,blurhash,width,height")
.order("id", { ascending: false })
.eq("campaign", config.campaign.name)
.eq("enabled", true);
if (country && country !== "?") {
query = query.eq("area", country);
}
let { data, error } = await query;
if (error) {
console.error(error);
return;
}
setPictures(data);
})();
}, [country, config.campaign.name, supabase]);
const CountrySelect = () => {
if (config.component.wall.country !== true) return null;
return (
<TextField
id="country"
select
variant="filled"
label="Country"
value={country}
onChange={e => setCountry(e.target.value)}
>
<MenuItem key="?" value="?">
Choose your country
</MenuItem>
{countries.map(option => (
<MenuItem key={option} value={option} />
))}
</TextField>
);
};
return (
<>
<Dialog
name={selected !== false ? pictures[selected].legend : "a"}
dialog={selected !== false}
close={handleClose}
>
{selected !== false && (
<img
className={classes.timg}
src={makeUrl(pictures[selected], campaign)}
alt={pictures[selected].legend}
/>
)}
</Dialog>
<CountrySelect options={countries} />
<Grid container spacing={1} justifyContent="center" alignItems="center">
{pictures.map((d, i) => (
<Grid key={d.hash} xs={12} sm={3} item onClick={() => select(i)}>
<img
className={classes.img}
onError={e => setBlurhash(e, d)}
onLoad={e => replaceBlur(e)}
loading="lazy"
src={makeUrl(d, campaign)}
alt={d.legend}
/>
<img
src={getBackground(d) || placeholder}
className={classes.bimg}
alt={d.legend}
/>
</Grid>
))}
</Grid>
</>
);
};
export default PictureWall;