Skip to content

Commit

Permalink
Visual popup settings
Browse files Browse the repository at this point in the history
  • Loading branch information
sarithapillai8 committed Apr 8, 2024
1 parent 6e78a21 commit 721deca
Show file tree
Hide file tree
Showing 12 changed files with 376 additions and 10 deletions.
26 changes: 23 additions & 3 deletions src/components/NotificationPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { useTranslation } from "react-i18next"
import GroupActivity from "./GroupActivity"
import { spliceActivity, spliceCTActivity } from "./Researcher/ActivityList/ActivityMethods"
import { Service } from "./DBService/DBService"

import VisualPopup from "./VisualPopup"
const useStyles = makeStyles((theme) => ({
root: {
width: "100%",
Expand Down Expand Up @@ -138,6 +138,8 @@ export default function NotificationPage({ participant, activityId, mode, tab, .
const [streakActivity, setStreakActivity] = useState(null)
const [openNotFound, setOpenNotFound] = useState(false)
const [tag, setTag] = useState(null)
const [visualPopup, setVisualPopup] = useState(null)

useEffect(() => {
setLoading(true)
setResponse(false)
Expand Down Expand Up @@ -173,7 +175,7 @@ export default function NotificationPage({ participant, activityId, mode, tab, .

const showStreak = (participant, activity) => {
setLoading(true)

setVisualPopup(null)
setStreakActivity(tag?.streak ?? null)
if (!!tag?.streak?.streak || typeof tag?.streak === "undefined") {
getEvents(participant, activity.id).then((streak) => {
Expand All @@ -191,6 +193,17 @@ export default function NotificationPage({ participant, activityId, mode, tab, .
}
}

const showVisualPopup = (activity) => {
Service.getUserDataByKey("activitytags", [activity?.id], "id").then((tags) => {
const tag = tags[0]
if (typeof tag?.visualSettings === "undefined" || !!tag?.visualSettings) {
setVisualPopup(tag?.visualSettings)
} else {
showStreak(participant, activity)
}
})
}

return (
<div style={{ height: "100%" }}>
{!!response && (
Expand Down Expand Up @@ -238,7 +251,9 @@ export default function NotificationPage({ participant, activityId, mode, tab, .
if (data === null) {
if (mode === null) window.location.href = "/#/"
else history.back()
} else if (!!data && !!data?.timestamp) showStreak(participant, activity)
} else if (!!data && !!data?.timestamp) {
showVisualPopup(activity)
}
}}
/>
))}
Expand Down Expand Up @@ -282,6 +297,11 @@ export default function NotificationPage({ participant, activityId, mode, tab, .
</Button>
</DialogActions>
</Dialog>
<VisualPopup
open={visualPopup?.checked ?? false}
image={visualPopup?.image}
showStreak={() => showStreak(participant, activity)}
/>
</div>
)
}
45 changes: 39 additions & 6 deletions src/components/Participant.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import { Service } from "./DBService/DBService"
import { useTranslation } from "react-i18next"
import Streak from "./Streak"
import locale_lang from "../locale_map.json"
import VisualPopup from "./VisualPopup"

export async function getImage(activityId: string, spec: string) {
return [
Expand Down Expand Up @@ -134,7 +135,6 @@ export default function Participant({

const [tab, _setTab] = useState(getTab())
const supportsSidebar = useMediaQuery(useTheme().breakpoints.up("md"))
const { enqueueSnackbar } = useSnackbar()
const [openDialog, setOpen] = useState(false)
const [hideCareTeam, setHideCareTeam] = useState(_hideCareTeam())
const [hiddenEvents, setHiddenEvents] = React.useState([])
Expand All @@ -143,6 +143,8 @@ export default function Participant({
const [loading, setLoading] = useState(false)
const [openComplete, setOpenComplete] = React.useState(false)
const [streak, setStreak] = useState(1)
const [visualPopup, setVisualPopup] = useState(null)
const [currentActivity, setCurrentActivity] = useState(null)
const { t, i18n } = useTranslation()

const tabDirection = (currentTab) => {
Expand Down Expand Up @@ -191,6 +193,7 @@ export default function Participant({
photo: img?.photo ?? null,
streak: img?.streak ?? null,
questions: img?.questions ?? null,
visualSettings: img?.visualSettings ?? null,
})
if (count === activities.length - 1) {
Service.addUserData("activitytags", data, true).then(() => {
Expand Down Expand Up @@ -226,7 +229,20 @@ export default function Participant({
}
}

const showVisualPopup = (activity) => {
Service.getUserDataByKey("activitytags", [activity?.id], "id").then((tags) => {
const tag = tags[0]
if (typeof tag?.visualSettings === "undefined" || !!tag?.visualSettings) {
setVisualPopup(tag?.visualSettings)
setCurrentActivity(activity)
} else {
showStreak(participant, activity)
}
})
}

const showStreak = (participant, activity) => {
setVisualPopup(null)
Service.getUserDataByKey("activitytags", [activity?.id], "id").then((tags) => {
const tag = tags[0]
setStreakActivity(tag?.streak ?? null)
Expand All @@ -251,17 +267,27 @@ export default function Participant({
<Box>
<Slide in={tab === "learn"} direction={tabDirection(0)} mountOnEnter unmountOnExit>
<Box mt={1} mb={4}>
<Learn participant={participant} activities={activities} activeTab={activeTab} showStreak={showStreak} />
<Learn
participant={participant}
activities={activities}
activeTab={activeTab}
showStreak={showVisualPopup}
/>
</Box>
</Slide>
<Slide in={tab === "assess"} direction={tabDirection(1)} mountOnEnter unmountOnExit>
<Box mt={1} mb={4}>
<Survey participant={participant} activities={activities} showStreak={showStreak} />
<Survey participant={participant} activities={activities} showStreak={showVisualPopup} />
</Box>
</Slide>
<Slide in={tab === "manage"} direction={tabDirection(2)} mountOnEnter unmountOnExit>
<Box mt={1} mb={4}>
<Manage participant={participant} activities={activities} activeTab={activeTab} showStreak={showStreak} />
<Manage
participant={participant}
activities={activities}
activeTab={activeTab}
showStreak={showVisualPopup}
/>
</Box>
</Slide>
<Slide in={tab === "portal"} direction={tabDirection(3)} mountOnEnter unmountOnExit>
Expand All @@ -272,7 +298,7 @@ export default function Participant({
allActivities={activities}
hiddenEvents={hiddenEvents}
enableEditMode={!_patientMode()}
showStreak={showStreak}
showStreak={showVisualPopup}
activitySubmitted={openComplete}
onEditAction={(activity, data) => {
setSurveyName(activity.name)
Expand Down Expand Up @@ -317,7 +343,7 @@ export default function Participant({
activities={activities}
visibleActivities={visibleActivities}
setVisibleActivities={setVisibleActivities}
showStreak={showStreak}
showStreak={showVisualPopup}
/>
</Box>
</Slide>
Expand Down Expand Up @@ -347,6 +373,13 @@ export default function Participant({
activity={streakActivity}
streak={streak}
/>
{!!visualPopup?.checked && (
<VisualPopup
open={visualPopup?.checked ?? false}
image={visualPopup?.image}
showStreak={() => showStreak(participant, currentActivity)}
/>
)}
</React.Fragment>
)
}
22 changes: 21 additions & 1 deletion src/components/Researcher/ActivityList/ActivityHeader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ import {
MenuItem,
FormControlLabel,
Checkbox,
Divider,
} from "@material-ui/core"
import { useSnackbar } from "notistack"
import { useTranslation } from "react-i18next"
import { useDropzone } from "react-dropzone"
import ActivityTab from "./ActivityTab"
import ActivityStreak from "./ActivityStreak"
import ActivityImage from "./ActivityImage"

function compress(file, width, height) {
return new Promise((resolve, reject) => {
Expand Down Expand Up @@ -63,6 +65,7 @@ export default function ActivityHeader({
const { enqueueSnackbar } = useSnackbar()
const [studyId, setStudyId] = useState(!!value ? value.study_id : study)
const [streak, setStreak] = useState(details?.streak ? details?.streak : null)
const [visualSettings, setVisualSettings] = useState(details?.visual_settings ? details?.visual_settings : null)
const [showFeed, setShowFeed] = useState(
typeof details?.showFeed !== "undefined" && details?.showFeed !== null ? details?.showFeed : true
)
Expand All @@ -75,8 +78,9 @@ export default function ActivityHeader({
studyId,
streak,
showFeed,
visualSettings,
})
}, [text, description, photo, studyId, streak, showFeed])
}, [text, description, photo, studyId, streak, showFeed, visualSettings])

const { acceptedFiles, getRootProps, getInputProps, isDragActive, isDragAccept } = useDropzone({
onDropAccepted: useCallback((acceptedFiles) => {
Expand Down Expand Up @@ -191,6 +195,22 @@ export default function ActivityHeader({
/>
</Grid>
<ActivityStreak onChange={(val) => setStreak(val)} value={details?.streak} />
<Divider />
{[
"lamp.jewels_a",
"lamp.jewels_b",
"lamp.spatial_span",
"lamp.maze_game",
"lamp.symbol_digit_substitution",
"lamp.spin_wheel",
].includes(activitySpecId) && (
<ActivityImage
onChange={(val) => setVisualSettings(val)}
activitySpecId={activitySpecId}
value={details?.visual_settings}
/>
)}
<Divider />
</Grid>
)
}
155 changes: 155 additions & 0 deletions src/components/Researcher/ActivityList/ActivityImage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import React, { useState, useCallback, useEffect } from "react"
import {
TextField,
makeStyles,
createStyles,
Theme,
Switch,
FormControlLabel,
Grid,
Divider,
Typography,
ButtonBase,
Icon,
Tooltip,
Box,
} from "@material-ui/core"
import { useSnackbar } from "notistack"
import Jewels from "../../../icons/VisualPopup/Jewels.svg"
import Maze from "../../../icons/VisualPopup/Maze.svg"
import SpatialSpan from "../../../icons/VisualPopup/SpatialSpan.svg"
import SpinWheel from "../../../icons/VisualPopup/SpinWheel.svg"
import Symbol_Digit from "../../../icons/VisualPopup/Symbol_Digit.svg"
import { useTranslation } from "react-i18next"
import { useDropzone } from "react-dropzone"
const useStyles = makeStyles((theme: Theme) =>
createStyles({
menuitemsul: {
width: "100%",
},
dividerRoot: { marginTop: 10 },
marginTop10: { marginTop: "10px" },
gridFlex: { display: "flex" },
})
)

function compress(file, width, height) {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.readAsDataURL(file)
const fileName = file.name
const extension = fileName.split(".").reverse()[0]?.toLowerCase()
reader.onerror = (error) => reject(error)
if (extension !== "svg") {
reader.onload = (event) => {
const img = new Image()
img.src = event.target.result as string
img.onload = () => {
const elem = document.createElement("canvas")
elem.width = width
elem.height = height
const ctx = elem.getContext("2d")
ctx.drawImage(img, 0, 0, width, height)
resolve(ctx.canvas.toDataURL())
}
}
} else {
reader.onload = (event) => {
resolve(reader.result)
}
}
})
}
export default function ActivityImage({ ...props }) {
const classes = useStyles()
const { t } = useTranslation()
const [image, setImage] = useState(props.value?.image ?? "")
const [checked, setChecked] = useState(props.value?.checked ?? false)
const { enqueueSnackbar } = useSnackbar()

const { acceptedFiles, getRootProps, getInputProps, isDragActive, isDragAccept } = useDropzone({
onDropAccepted: useCallback((acceptedFiles) => {
compress(acceptedFiles[0], 64, 64).then(setImage)
}, []),
onDropRejected: useCallback((rejectedFiles) => {
if (rejectedFiles[0].size / 1024 / 1024 > 5) {
enqueueSnackbar(`${t("Image size should not exceed 5 MB.")}`, { variant: "error" })
} else if ("image" !== rejectedFiles[0].type.split("/")[0]) {
enqueueSnackbar(`${t("Not supported image type.")}`, { variant: "error" })
}
}, []),
accept: "image/*",
maxSize: 2 * 1024 * 1024 /* 5MB */,
})

useEffect(() => {
props.onChange({ image, checked })
}, [image, checked])

useEffect(() => {
switch (props.activitySpecId) {
case "lamp.jewels_a":
case "lamp.jewels_b":
setImage(Jewels)
break
case "lamp.spatial_span":
setImage(SpatialSpan)
break
case "lamp.maze_game":
setImage(Maze)
break
case "lamp.symbol_digit_substitution":
setImage(Symbol_Digit)
break
case "lamp.spin_wheel":
setImage(SpinWheel)
break
}
}, [])

return (
<Grid item lg={12} md={9} xs={12}>
<Typography variant="h6">{`${t("Visual popup settings")}`}</Typography>
<Divider classes={{ root: classes.dividerRoot }} />
<Grid container spacing={2}>
<Grid item alignItems="center" lg={3} sm={3} xs={12} className={classes.gridFlex}>
<FormControlLabel
control={<Switch checked={checked} onChange={() => setChecked(!checked)} name="image" />}
label={!!checked ? `${t("Visual popup on")}` : `${t("Visual popup off")}`}
/>
</Grid>

{!!checked && (
<Grid container style={{ marginLeft: "-15px" }}>
<Tooltip
title={
!image
? `${t("Drag a photo or tap to select a photo.")}`
: `${t("Drag a photo to replace the existing photo or tap to delete the photo.")}`
}
>
<Box
{...getRootProps()}
width={154}
height={154}
border={1}
borderRadius={4}
borderColor={!(isDragActive || isDragAccept || !!image) ? "text.secondary" : "#fff"}
bgcolor={isDragActive || isDragAccept ? "text.secondary" : undefined}
color={!(isDragActive || isDragAccept || !!image) ? "text.secondary" : "#fff"}
style={{
background: !!image ? `url(${image}) center center/contain no-repeat` : undefined,
}}
>
<ButtonBase style={{ width: "100%", height: "100%" }} onClick={() => !!image && setImage(undefined)}>
{!image && <input {...getInputProps()} />}
<Icon fontSize="large">{!image ? "add_a_photo" : "delete_forever"}</Icon>
</ButtonBase>
</Box>
</Tooltip>
</Grid>
)}
</Grid>
</Grid>
)
}
Loading

0 comments on commit 721deca

Please sign in to comment.