Hey! I'm Paul

The occasional

developer.

Otherwise a Solutions Engineer at Pulsepoint.

Projects.

A flask application to give Pulsepoints LIFE api an UI for ease of use for their clients.

Life NPI API

A flask application to give Pulsepoints LIFE api an UI for ease of use for their clients.
Published on
A prototype for creating audience segments and relationships, built in React and Supabase

Signal Journey Prototype

A prototype for creating audience segments and relationships, built in React and Supabase
Published on
ReactFast contacts. CRUD app built with a FastAPI / SQLite backend with a pretty React & TailwindCSS front end.

ReactFast Contacts

ReactFast contacts. CRUD app built with a FastAPI / SQLite backend with a pretty React & TailwindCSS front end.
Published on

Writing.

Creating a dark theme switch with Tailwind & Framer Motion

Dark themes are all the rage, most of the sites you visit today will have some sort of dark theme switch. Allowing you to switch between a light theme and a dark theme on the site you're visiting. I will hopefully explain how to create an awesome switch using a little bit of Tailwind and Frame Motion. Framer motion is an animation library for React, it's super cool and I recommend that you check it out. This is what we will be knocking up today. First let's install framer and then import it into our component ```bash npm install framer-motion ``` Once installed let's add it to our component. ```js import { motion } from "framer-motion" ``` We need to then import `useState` from React so we can capture the state of `isOn` our component should look something like this now. ```js import React, { useState} from 'react' import {motion} from 'framer-motion' export default function DarkModeSwitch(){ const [isOn, setIsOn] = useState(false) return() } ``` Above we have a state of `false` to `isOn` we're currently returning nothing but let's change that now. If you take a look at the Framer [example](https://codesandbox.io/s/framer-motion-2-layout-animations-kij8p?from-embed=&file=/src/App.js) it looks very straightforward. With the example, they're using vanilla CSS. Let's use Tailwind CSS with ours. First, we need to create a container `div` for our switch. ```js <div className={`flex-start flex h-[50px] w-[100px] rounded-[50px] bg-zinc-100 p-[5px] shadow-inner hover:cursor-pointer dark:bg-zinc-700 ${ isOn && 'place-content-end'}`}></div> ``` I have included a ternary operator in my `className` string this is because we need to conditional move the switch when `isOn` is true or false. ```js ${isOn && 'place-content-end'}`} ``` We're using **place-content-end** here which allows us to place the element at the end of its container. This is similar to using `justify-end` in Tailwind. The other styles in `className` are just for my preference you can change these to what you like. Now we have our container div, let's give it some magic. We need to give it an `onClick` attribute. So let's do that now. ```js <div onClick={toggleSwitch} className={`flex-start flex h-[50px] w-[100px] rounded-[50px] bg-zinc-100 p-[5px] shadow-inner hover:cursor-pointer dark:bg-zinc-700 ${ isOn && 'place-content-end'}`}></div> ``` As you can see we have given the `onClick` a function to execute so let's add that and the div container into our component. ```js import React, { useState} from 'react' import {motion} from 'framer-motion' export default function DarkModeSwitch(){ const [isOn, setIsOn] = useState(false) const toggleSwitch = () => setIsOn(!isOn) return( <div onClick={toggleSwitch} className={`flex-start flex h-[50px] w-[100px] rounded-[50px] bg-zinc-100 p-[5px] shadow-inner hover:cursor-pointer dark:bg-zinc-700 ${ isOn && 'place-content-end'}`}></div> ) } ``` What are we doing with then `toggleSwitch` why aren't we setting it true? I will explain that later but for now let's leave it the way it is. Now time to add the switch. With the container div we should just have a rectangle with rounded edges, let's change that now. This is where motion comes in, we need to create another `div` but this time it will be a `motion.div` this allows us to give it some frame magic. Let's add that below with some classes from Tailwind. ```js import React, { useState} from 'react' import {motion} from 'framer-motion' export default function DarkModeSwitch(){ const [isOn, setIsOn] = useState(false) const toggleSwitch = () => setIsOn(!isOn) return( <div onClick={toggleSwitch} className={`flex-start flex h-[50px] w-[100px] rounded-[50px] bg-zinc-100 p-[5px] shadow-inner hover:cursor-pointer dark:bg-zinc-700 ${ isOn && 'place-content-end'}`}> <motion.div className="flex h-[40px] w-[40px] items-center justify-center rounded-full bg-black/90" layout transition={spring} > </motion.div> </div> ) } ``` We now have out `motion.div` with the additional attributes of `layout` and `transition` let's go through those now. **layout**: `boolean` | `"position"` | `"size"` If `true`, this component will automatically animate to its new position when its layout changes. More info [here](https://www.framer.com/docs/component/###layout) **transition**: Transition Defines a new default transition for the entire tree. More info [here](https://www.framer.com/docs/motion-config/###transition) Let's add our `transition` animations, this is going to be an object like so. ```js const spring = { type: 'spring', stiffness: 700, damping: 30, } ``` - [spring](https://www.framer.com/docs/transition/#spring): An animation that simulates spring physics for realistic motion. - [stiffness](https://www.framer.com/docs/transition/###stiffness): Stiffness of the spring. Higher values will create more sudden movement. Set to 100 by default. - [damping](https://www.framer.com/docs/transition/###damping): Strength of opposing force. If set to 0, spring will oscillate indefinitely. Set to 10 by default. After adding our `motion.div` and `spring` object we should have something like this: ```js import React, { useState} from 'react' import {motion} from 'framer-motion' export default function DarkModeSwitch(){ const [isOn, setIsOn] = useState(false) const toggleSwitch = () => setIsOn(!isOn) const spring = { type: 'spring', stiffness: 700, damping: 30, } return( <div onClick={toggleSwitch} className={`flex-start flex h-[50px] w-[100px] rounded-[50px] bg-zinc-100 p-[5px] shadow-inner hover:cursor-pointer dark:bg-zinc-700 ${ isOn && 'place-content-end'}`}> <motion.div className="flex h-[40px] w-[40px] items-center justify-center rounded-full bg-black/90" layout transition={spring} > </motion.div> </div> ) } ``` This would be our finished switch, but wait there is more..what about the icons and the cool click animation??? Ok, so let's install [React Icons](https://react-icons.github.io/react-icons/) and grab those icons. Install React Icons via npm. ```bash npm install react-icons --save ``` I have chosen the following icons, they're from the Remix library. Let's add those now. ```js import React, { useState} from 'react' import {motion} from 'framer-motion' import {RiMoonClearFill, RiSunFill} from 'react-icons/ri' ... ``` Now we need to place our icons, inside of our toggle switch. Our toggle switch is the `motion.div` we made earlier. This stage is pretty simple, we just need to create another `motion.div` inside of the parent `motion.div` and give it some ternary operators and a `whileTape` attribute like so: ```js <motion.div whileTap={{rotate: 360}}> {isOn ? (<RiSunFill className="h-6 w-6 text-yellow-300" />) : (<RiMoonClearFill className="h-6 w-6 text-slate-200" />)} </motion.div> ``` You can give your icons your own styling but this is how I have set mine up. Using the ternary operator allows us to switch the icon on the status of `isOn` we should now have the following: ```js import {motion} from 'framer-motion' import React, {useState} from 'react' import {RiMoonClearFill, RiSunFill} from 'react-icons/ri' export default function DarkModeSwitch(){ const [isOn, setIsOn] = useState(false) const toggleSwitch = () => setIsOn(!isOn) const spring = { type: 'spring', stiffness: 700, damping: 30, } return( <div onClick={toggleSwitch} className={`flex-start flex h-[50px] w-[100px] rounded-[50px] bg-zinc-100 p-[5px] shadow-inner hover:cursor-pointer dark:bg-zinc-700 ${ isOn && 'place-content-end'}`}> <motion.div className="flex h-[40px] w-[40px] items-center justify-center rounded-full bg-black/90" layout transition={spring} > <motion.div whileTap={{rotate: 360}}> {isOn ? (<RiSunFill className="h-6 w-6 text-yellow-300" />) : (<RiMoonClearFill className="h-6 w-6 text-slate-200" />)} </motion.div> </motion.div> </div> ) } ``` ### Adding into Local Storage Now we have a working component, but it's not completely done we need to handle our dark mode with `localStrogae` so the user can keep their preference for next time. Reading over the [Tailwind Docs](https://tailwindcss.com/docs/dark-mode) on dark mode, we need to be able to toggle dark mode manually. To do this we need to add ` darkMode: 'class',` into our `tailwind.config.js` file. Something like this. ```js module.exports = { darkMode: 'class', ... ``` Now we can toggle dark mode manually via the switch. I have used the example on the Tailwind website for supporting light mode, dark mode, as well as respecting the operating system preference. However I have tweaked it a little bit, remember the state `const [isOn, setIsOn] = useState(false)` lets change that to read `localStorage` and check if the `theme` is set to `light` ```js // before const [isOn, setIsOn] = useState(false) // after const [isOn, setIsOn] = useState(() => { if (localStorage.getItem('theme') === 'light') { return true } else { return false } }) ``` Instead of the state returning `false` it fires off a function and checks if the theme within local storage is `light` if it is, `isOn` is true if not it's false. Now let's use the state of `isOn` to manage the theme within local storage. ```js if (isOn) { document.documentElement.classList.remove('dark') localStorage.setItem('theme', 'light') } else { document.documentElement.classList.add('dark') localStorage.setItem('theme', 'dark') } ``` The above will do the following: ```html <!-- Dark mode not enabled --> <html> <body> <!-- Will be white --> <div class="bg-white dark:bg-black"> <!-- ... --> </div> </body> </html> <!-- Dark mode enabled --> <html class="dark"> <body> <!-- Will be black --> <div class="bg-white dark:bg-black"> <!-- ... --> </div> </body> </html> ``` Lastly, we add the following which allows us to avoid [FOUC](https://en.wikipedia.org/wiki/Flash_of_unstyled_content#:~:text=A%20flash%20of%20unstyled%20content,before%20all%20information%20is%20retrieved.) when changing themes of page loads ```js if ( localStorage.theme === 'light' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: light)').matches) ) { document.documentElement.classList.add('dark') } else { document.documentElement.classList.remove('dark') } ``` So that's it...our final component should look like this... ```js import {motion} from 'framer-motion' import React, {useState} from 'react' import {RiMoonClearFill, RiSunFill} from 'react-icons/ri' export default function DarkModeSwitch(){ const [isOn, setIsOn] = useState(() => { if (localStorage.getItem('theme') === 'light') { return true } else { return false } }) const toggleSwitch = () => setIsOn(!isOn) const spring = { type: 'spring', stiffness: 700, damping: 30, } if (isOn) { document.documentElement.classList.remove('dark') localStorage.setItem('theme', 'light') } else { document.documentElement.classList.add('dark') localStorage.setItem('theme', 'dark') } if ( localStorage.theme === 'light' || (!('theme' in localStorage) && window.matchMedia('(prefers-color-scheme: light)').matches) ) { document.documentElement.classList.add('dark') } else { document.documentElement.classList.remove('dark') } return( <div onClick={toggleSwitch} className={`flex-start flex h-[50px] w-[100px] rounded-[50px] bg-zinc-100 p-[5px] shadow-inner hover:cursor-pointer dark:bg-zinc-700 ${ isOn && 'place-content-end'}`}> <motion.div className="flex h-[40px] w-[40px] items-center justify-center rounded-full bg-black/90" layout transition={spring} > <motion.div whileTap={{rotate: 360}}> {isOn ? (<RiSunFill className="h-6 w-6 text-yellow-300" />) : (<RiMoonClearFill className="h-6 w-6 text-slate-200" />)} </motion.div> </motion.div> </div> ) } ```
Published on

Setting up Docker with Pipenv

I have always struggled with Docker as I mainly build front end web apps, where I have used services like Vercel to host my applications. Sometimes for work, I am required to build some scripts that need to run in a container. And this where sh*t fits the fan generally. But no more!! I think I have finally cracked it, hello `docker-compose` I have always seen these files but never really used them. I'll give you a rundown of how I am using my `Dockerfile` and `docker-compose` together to get python running with pipenv. ```bash # For more information, please refer to https://aka.ms/vscode-docker-python FROM python:3.9-slim ENV VAR1=10 # Keeps Python from generating .pyc files in the container ENV PYTHONDONTWRITEBYTECODE=1 # Turns off buffering for easier container logging ENV PYTHONUNBUFFERED=1 # Install & use pipenv COPY Pipfile Pipfile.lock ./ RUN python -m pip install --upgrade pip RUN pip install pipenv && pipenv install --dev --system --deploy WORKDIR /app COPY . /app # Creates a non-root user and adds permission to access the /app folder RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app USER appuser # During debugging, this entry point will be overridden. For more information, please refer to https://aka.ms/vscode-docker-python-debug CMD ["python", "main.py"] ``` This is my Docker file for running Python with Pipenv works well enough. This is generally the boilerplate from VSC when you add a Docker file to the workspace. I have only really replaced the `requirements.txt` and `pip` stuff with the below ```bash COPY Pipfile Pipfile.lock ./ RUN python -m pip install --upgrade pip RUN pip install pipenv && pipenv install --dev --system --deploy ``` Which in turns copies my `Pipfile` & `Pipfile.lock` and then installs my dependencies. If you have a file named .env in your project, it’s only used to put values into the docker-compose.yml file which is in the same folder. Those are used with Docker Compose and Docker Stack. It has nothing to do with ENV, ARG, or anything Docker-specific. The key-value pairs, in your `.env` file are used to substitute dollar-notation variables in the docker-compose.yml file. It’s kind of a pre-processing step, and the resulting temporary file is used. This is a nice way to avoid hard-coding values. You can also use this to set the values for environment variables, by substituting the string, but that does not happen automatically. Here is an example docker-compose.yml file, relying on values provided from a .env file: ```yaml version: '3' services: plex: image: linuxserver/plex environment: - env_var_name=${VARIABLE_NAME} # here it is ``` > Hint: When working with an .env file, you can debug your docker-compose.yml files quite easily. Just type `docker-compose config`. This way you’ll see how the docker-compose.yml file content looks after the substitution step has been performed without running anything else. Once you're done just run `docker-compose -f docker-compose.yaml up` this will run a container with your image and all the specified env variables. Like anything it's easy when you understand it.
Published on

Stop using multiple states for status

When it comes to fetching API data in React using `fetch` there can be ways to update the user or run conditional code. This can generally mean using a selection of different states. Which can potentially get messy and complicated. You could have something like this: ```js const [data, setData] = useStatus([]) const [success, setSuccess] = useState('') const [loading, setLoading] = useState('') const [rejected, setRejected] = useState('') const [error, setError] = useState('') ``` Which in turn could be like the following: ```js useEffect(() => { setLoading('loading') // state 1 fetch('http://some-api-url') .then(response => { setData(response) setSuccess('success') // state 2 }) .catch(err => { setError(err) setRejected('rejected') // state 3 }) }, []) ``` With the above we have three states to manage and keep an eye on. When it comes to using this conditionally it's going to be a pain in the ass. Something like this. ```js if (success === 'success') { //do something } else if ( loading === 'loading' ) { // do something } else if (rejected === 'rejected') { // do something } ``` Now wouldn't it be easier to use a `status` state instead of worrying about 3 other states. We can include something like a `const [status, setStatus] = useStatus('idle')` and remove all the other states apart from the `setData` and `setError` we need the error state so we can handle the displaying of error messages. So now we have the following which is going to be much cleaner and easier to manage. ```js const [data, setData] = useStatus([]) const [error, setError] = useState('') const [status, setStatus] = useState('idle') useEffect(() => { setStatus('loading') fetch('http://some-api-url') .then(response => { setData(response) setStatus('success') }) .catch(err => { setError(err) setStatus('rejected') }) }, []) if (status === 'success') { // do something } else if ( status === 'loading' ) { // do something } else if (status === 'rejected') { // do something } ``` For me this is much a cleaner way of doing things, I know it's not that much different from using multiple states. However, we only have one state to deal with instead of three. I got inspired to do things this way by Kent C Dodds. More information [here](https://kentcdodds.com/blog/stop-using-isloading-booleans)
Published on

My story.

I currently work at Pulsepoint. in the solutions engineering team, before his time in SE he was an Account Director but now works from home full time in the SE team after coming back to the company in 2020. Pulsepoint has have recently been bought by Webmd.

I currently live in West London with my wife and dog Charlie (aka Chumbo, that's a story for another time).

I've has always had a fascination with technology, whether it is programming (Python or JavaScript) or something data orientated especially when it comes to sports, I'm always on Strava checking out my sports data.

I enjoy many sports but has a thing for endurance ones, especially cycling. He's also a good cook but enjoys eating out much more than cooking inside. Although he will stay inside when his wife cooking.

Tools.

Coding.

  • VSCodeLove VSC I switched to Pycharm but soon switched back.
  • Dracula ProEven though I made my own VSC theme I still keep going back to Dracula.
  • Fira CodaI love this monospaced font the ligtures are cool too.

Terminal.

  • HyperMoved from iTerm, not as powerful but it is built in JS.
  • Oh My ZshIt just works I also use SpaceShip with it too.
  • FigSupers app that adds VSCode-style autocomplete to the terminal.

Apps.

  • httpieAwesome HTTP client, easier to use than Postman.
  • DataGripMy weapon of choice, it is a great IDE for SQL.
  • FigmaI am no designer but I love Figma, it is a great tool for mocking ideas.
2022 No rights reserved.
3D Heart