Tiny Wins

Small victories worth celebrating because they make the journey worthwhile.

36
Total Wins
12/9/2022

If you want the union type of all the types of the values in another type in typescript you can use the keyof operator.

type Thing = {
  foo: string;
  bar: boolean;
}

type ThingTypes = Thing[keyof Thing];
// string | boolean
6/7/2022

If you have force pushed changes to a git branch on the remote and want to pull that branch into your local you can do it in two commands.

git fetch && git reset --hard @{u}

This will fetch the latest changes then reset the current branch with the latest of that same branch from the origin.

4/16/2022

The other day I learned a neat trick. Note this will work in many other editors as well. I needed to remove the type case at the end of an object in TypeScript (JavaScript) so I created a regex to find what I wanted, an export default {, then whatever including line breaks, then } as Meta. I grouped the part before the as Meta and used the group identifier $1 in the VSCode replace box to replace all occurrences of my regex with everything before the as Meta.

Here's my find:

(export default \{(.|\n)*\})( as Meta)

Notice the parentheses around the whole first part separating it from the as Meta.

Then in the replace just put $1 to replace all occurrences with the content of the first group.

2/13/2022

The other day I was looking through a repo and stumbled across someone using Vercel's open graph image generator, something I had no idea existed. I was able to set it up and replace my own implementation of something similar on my blog in an evening. My implementation involved generating images at build time with Gatsby, why I ever thought that was a good idea is beyond me.

This allows you generate unique open graph images for each one of your blog posts or pages and set it in meta tags. The meta tag in my blog posts now looks like this:

<meta
  property='og:image'
  content={OpenGraph.generateImageUrl(post.frontmatter.title)}
/>

Small tip here is to fork and deploy your own version of this repo so you can change the default image. Change this line: if (!images[0].startsWith('https://assets.vercel.com/') && !images[0].startsWith('https://assets.zeit.co/')) { in api/_lib/parser.ts.

P.S. Also a friendly reminder that this will not work if your pages are not viewable server-side meaning you're using straight Vue or React to render your entire site. Each page needs to have a viewable html file stored, this is static generation or server side generation.

1/1/2022

Sometimes you have single digit numbers that your want to add a leading 0 to for whatever reason. For example if you're dealing with dates and aren't using a date library with formatting.

This simple script will take the index of the month you have (where January = 0) and return '01' instead of '1' and will ignore numbers with 2 digits.

const twoDigitMonth = ("0" + (monthIndex + 1)).slice(-2);

Effectively this adds a 0 at the beginning of the string and returns 2 characters starting from the end of the string.

5/17/2021

This last weekend I learned how window balancers work. My windows are Anderson brand but I think it's similar for most other windows. The problem was the pane continualy falling down when I tried to open them. For years I've just been opening the top window since the bottom wouldn't stay open. I finally was able to remove both panes, slide the little cover down to expose the balancer inside the sash, and reconnect the string on the balancer to the balance shoe (the thing that slides up and down that the window sash sits in). Somehow the string had become disconnected so the spring in the balancer wasn't doing anything to hold the windows up. It was a bit of a pain but didn't take too long. Just surprised there was very little documentation on this online. Pretty sure I have Anderson 400 Series Tile-Wash double hung windows.

This picture shows the string connecting to the balance shoe.

Balance Shoe

This video gives a good visual of what I'm talking about although isn't really the problem I had: https://youtu.be/kjJ50qKMaqw

5/6/2021

import Link from 'gatsby-link';

I was gonna write this here but then just made a blog post,

Click here to read
5/3/2021

Recently learned you can view up to 49 people silmultaneously in Google Meet. Click the three dots in the bottom right corner and click "Change Layout". In that modal click "Tiled" and move the slider all the way to the right. Now you can look at everyone picking their nose at the same time!

5/3/2021

I was having issues running Puppeteer on my new M1 Apple Mac. Turns out I was using an old version of Node and Puppeteer. Upgrading to Node >v16 and Puppeteer >v9 fixed the issue.

Thanks to David Bailey for the fix.

4/30/2021

If you're using Obsidian for editing markdown files there's a sweet keyboard shortcut for checking checkboxes in the edit mode. You can also use this to check multiple checkboxes at the same time instead of entering an x in each [ ].

Just place your cursor on the line you want to check in edit mode and type Cmd + Enter. To check multiple items highlight multiple lines before hitting Cmd + Enter.

4/30/2021

So the other day I was updating my personal site to Node v16 and I noticed I couldn't install node-sass. I thought it was an m1 issue so was googling down that path but learned that people use a package called sass now instead of node-sass. So if you're having troubles with node-sass make the switch. sass doesn't rely on building native C code.

4/18/2021

Learned today that you can skip items when destructuring an array by using commas and leaving the index you want to skip over blank. This can be really helpful for certain api responses that are structured in an array, but you only want the second item.

For example:

const response = [responseInfo, body, error];

const [, body] = response;

In this destructuring we only want the second item from the array. Doing this will avoid linting errors from unused variables just to get to the index you wanted.

3/24/2021

Sometimes you want to strip off gmail + additions from an email. You probably don't want to store it this way because people should definitely be allowed to use + emails for filtering etc. but you may want to remove it to check for duplicates or other use cases. Here's the regex for doing that.

const regex = /\+(.*)(?=@)/gm;
const strippedEmail = email.replace(regex, '');
3/20/2021

I upgraded Gatsby to V3 on my personal site and am now using the new gatsby-image-plugin. Upgrade wasn't too bad 😃 took maybe an hour total. Maybe a little more including the new gatsby image plugin initial research.

The new image plugin is pretty intuitive and I like the move to StaticImage and DynamicImage. Use one if your image isn't based on dynamic data and the other if it is. There hasn't been a more clearly named component 🔥

3/12/2021

I love that instead of doing this:

.container {
  display: flex;

  & > .mat-card {
    flex: 1;
    margin-right: 30px;

    &:last-of-type {
      margin-right: 0;
    }
  }
}

I can just do this:

.container {
  display: grid;
  gap: 30px;
  grid-template-columns: repeat(3, 1fr);
}
1/19/2021

If you're using NextJS you're probably using the getStaticProps function or the getServerSideProps function on your pages to fetch data to populate your page props. Something I discovered the other day is that you can't pass some types in your props object to your React component including the Date type. In my particular application I'm using Prisma to get objects from my database that include createdAt and updatedAt dates.

My code looks a little something like this:

export const getStaticProps: GetStaticProps = async () => {
  const users = await prisma.user.findMany();
  return { props: { users }, revalidate: 1 };
};

However, this was a problem because getStaticProps serializes the returned object and Date (along with a few other types) can't be serialized. You'll get an error like ("[object Date]") cannot be serialized as JSON. Please only return JSON serializable data type. So I found this discussion on the topic https://github.com/vercel/next.js/discussions/11498. I ended up installing a package called babel-plugin-superjson-next and superjson and added a .babelrc file with these contents:

{
  "presets": ["next/babel"],
  "plugins": ["superjson-next"]
}

This just fixes the issue as described in their docs: https://github.com/blitz-js/superjson#using-with-nextjs.

Should also note here you could fix this by manually handling each Date type in your getStaticProps method. You could map over each returned object and convert the Date to a string before returning that array.

1/13/2021

Learned that finding a simple (~free) Postgres hosted solution is kinda hard. Heroku kinda works but for a Vercel-hosted NextJS app it was running out of connections. Finally set up a Docker VM on Google Cloud (GCE) and that worked pretty well even though it ended up being about $4/mo. Seems there might be some autoscaling spin down to 0 options that might work too.

5/27/2020

Added webmention counts to my blog posts and tiny wins.

5/10/2020

Sometimes you have content in a before element that you want to appear on top of a background image but behind other elements on the page. In my situation I had an image with a css triangle used as a mask over the image but wanted text to be able to appear on top of the triangle.

The image below shows what I was going for. There is a white triangle masking the image and the text is visible on top of the triangle even though it is position absolute. The key here is making sure the :before element has z-index: 0 and any of the text elements are either in a flex container or have position: relative with z-index: 1.

Example

<div class="body-wrapper">
  <h1 class="heading">@itwasmattgregg</h1>
</div>
.body-wrapper {
  background-image: url('https://codegregg.com/images/matt.jpg');
  background-size: contain;
  background-position: right;
  background-repeat: no-repeat;
  height: 600px;
  position: relative;
}

.body-wrapper::before {
  content: '';
  position: absolute;
  top: 0;
  height: 0;
  width: 0;
  z-index: 0;
  right: 440px;
  border-top: 600px solid #fff;
  border-right: 160px solid transparent;
}

.heading {
  z-index: 1;
  position: relative;
}
5/8/2020

Was having troubles with my functions redirect rule in netlify.toml file with my Gatsby site. Originally it looked like this:

[[redirects]]
  from = "/api/*"
  to = "/.netlify/functions/:splat"
  status = 200

But gatsby was taking over and serving a 404 for this route. But if you add the force key to the redirect everything works fine. Add it like this:

[[redirects]]
  from = "/api/*"
  to = "/.netlify/functions/:splat"
  status = 200
  force = true
5/5/2020

Spent the all of today creating social cards that are generated at build time for my Gatsby blog. Most of my frustration came with trying to load a custom font into my template loaded by puppeteer. Finally figured out that .otf fonts don't like being base64 encoded as much as .woff fonts do. I should have turned off my local font file sooner to debug this 🤦‍♂. The other frustration was with the facebook debugger which was returning a 404 for the pages I was testing. Turns out you don't get the path variable in gatsby template files at the server rendered phase. Had to switch to using the useLocation() hook to concat with siteUrl for my og:url tag.

Will write a full blog post tutorial at some point.

4/27/2020

Learned how to add partials to emotion styled components with props. This way you can add a bunch of css if a prop is true. Boy is React cool.

import styled from '@emotion/styled';
import { css } from '@emotion/core';

const isAchievementPartial = (props: any) =>
  props.isAchievement &&
  css`
    padding: 20px 5px 5px 5px;
    border: 2px solid rgb(36, 234, 182);
    border-radius: 11px;
    position: relative;
    overflow: hidden;

    &:before {
      content: 'Achievement';
      position: absolute;
      top: 0;
      left: 0;
      background: rgb(36, 234, 182);
      font-size: 12px;
      padding: 1px 6px;
      font-weight: bold;
    }
  `;

const Message = styled.div<any>`
  display: flex;
  margin-bottom: 10px;
  ${isAchievementPartial}
`;

<Message isAchievement={true} />
4/26/2020

Made an awesome React SVG circle gauge with emotion props.

const SkillGauge = styled.svg`
  width: 120px;
  height: 120px;
  grid-column: 1/1;
  grid-row: 1/1;
`;

const SkillGaugeCircle = styled.circle`
  stroke-width: 4px;
  transform: rotate(-90deg);
  transform-origin: 50% 50%;
  stroke-dasharray: ${props => props.circumference} ${props =>
      props.circumference};
  stroke-dashoffset: ${props => props.offset};
  stroke-linecap: 'round';
`;

const radius = 58;
const circumference = radius * 2 * Math.PI;
const offset = circumference - (xp / 100) * circumference;

const gaugeColor = xp => {
  if (xp <= 33) {
    return '#afb6c0';
  } else if (xp > 33 && xp < 66) {
    return '#637184';
  } else {
    return '#1a344d';
  }
};

<SkillGauge>
  <SkillGaugeCircle
    stroke={gaugeColor(xp)}
    fill='transparent'
    r={radius}
    circumference={circumference}
    offset={offset}
    cx='60'
    cy='60'
  />
</SkillGauge>;
4/16/2020

Just got my old wedding website up at http://gregg-wedding.herokuapp.com/. I forgot how easy Laravel is to use. Especially making models, using factories to generate fake seeding in the db, and passing data to views. It's seriously a joy and took me almost no time at all to dive back in.

4/10/2020

Wrote an awesome VS Code snippet for starting new tiny win files in mdx. This snippet takes the filename and capitalizes words as well as removes hyphens and replaces them with spaces. It also inserts the current date and time for frontmatter.

{
  "tiny wins": {
    "prefix": "tinywin",
    "body": [
      "---",
      "  title: '${1:${TM_FILENAME_BASE/(([\\w]*)([^\\w\\s]*)*(\\-)*)/${2:/capitalize}${3:+ }/g}}'",
      "  date: '$CURRENT_YEAR-$CURRENT_MONTH-${CURRENT_DATE}T$CURRENT_HOUR:$CURRENT_MINUTE:00Z'",
      "---",
      "",
      "$0"
    ],
    "description": "Create a new tiny wins mdx file"
  }
}
4/10/2020

Implemented basic search in django using SearchFilter. You can add something this simple to a ViewSet to make fields searchable the search with /apiEndpoint/?search=searchterm

filter_backends = [filters.SearchFilter]
search_fields = ['username', 'email']
1/13/2020

Successfully made it through my third bone marrow biopsy. It's painful and weird but thankfully not too painful. I just have such an aversion to them for some reason.

12/19/2019

I found a hidden issue in a python API. My backend dev for this project has been on PTO lately so I've had to take over for him. I feel accomplished in being able to handle some small backend tasks when it's something I haven't really done in ages.

12/18/2019

So I might not know all the depths of how caching works... who does? But I did figure out how to run cron jobs, edit entities, and flush memcache in Google App Engine. For a front ender that was a lot for one day.

12/11/2019

I made my first Github action to deploy a static VueJS site to Firebase hosting.

11/21/2019

I solved a nasty bug in django I was having with getting the site_url for transactional email. I didn't realize this site_url came from the Site object stored in the database and set via the admin panel. I had it set to the production url even though it was the UAT site.

11/18/2019

I learned how to split different filesystem sources in gatsby and use childMdx to get the transformed mdx child node.

9/18/2019

Built a puppeteer scraper for allergy data.

Transformed it into a serverless function that proxies the request.

9/5/2019

Got my first public PR approved and merged

9/1/2019

Got invited to consult with a startup called Beingful by one of their board members.

Accepted VP of Tech role.

7/31/2019

Shipped my first piece of open source software, a Gatsby theme