sneakycrow

code, dogs, and teleporting, flying, cowboy elves.

Today I stumbled across a website called The Outline via an article. I went to the homepage of the website and was faced with one of the coolest hero designs I've seen.

Now, I will admit that initially I wasn't super impressed. Yea, it looks really cool, but I assumed that the background image was just that, a background image. But then I noticed something. If you look at the top portion of the background image it actually covers the border. Now, it could just be some complex z-indexing, but I wanted to know.

Upon inspect, I discovered that the duck background was actually a 2k image of ducks, without masking. The masking itself is a completely separate image. The developers are using a CSS property I hadn't heard of until today called mask-image.

According to MDN:

The mask-image CSS property sets the image that is used as mask layer for an element.

This is an extremely interesting feature. Of course, I'd love to play with it, but I also was curious what the browser support was. To my surprise, it's actually pretty damn good! It looks like Safari has a few minor issues.

The feature is still considered experimental, but considering support and just how damn cool it is, I really want to check it out and see what I can do with it.

I added a new dynamic twitch link to the website. When I'm not live on Twitch, it's just a normal link. When I go live it turns purple and gets a red “recording dot” next to it.

It's a pretty simple component, I'm super happy about it. Here's the source:

import React, { useEffect, useState } from 'react';
import fetch from 'isomorphic-unfetch';
import styled from 'styled-components';

const StyledTwitchLink = styled.a`
  color: ${props => (props.isLive ? props.theme.palette.twitch : props.theme.palette.black)};
  padding: 8px 16px;
  display: block;
  text-transform: uppercase;
  font-size: 0.8em;
  letter-spacing: -0.5px;
  font-weight: 700;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: color 0.25s ease-in-out;
  &:visited {
    color: ${props => (props.isLive ? props.theme.palette.twitch : props.theme.palette.black)};
  }
  &:hover {
    color: ${props => props.theme.palette.twitch};
    cursor: pointer;
  }
  &::after {
    content: '';
    width: 8px;
    height: 8px;
    border-radius: 50%;
    display: ${props => (props.isLive ? 'block' : 'none')};
    background-color: ${props => props.theme.palette.red};
    margin-left: 4px;
  }
  @media screen and (max-width: ${props => props.theme.layout.mobileMaxWidth}) {
    font-size: 1.6em;
    padding: 16px;
  }
`;

const TwitchLink = () => {
  const [isLive, setLive] = useState(false);
  useEffect(() => {
    fetch('https://api.twitch.tv/helix/streams?user_login=sneakycr0w', {
      headers: {
        'Client-ID': '0bhs4en3k15l4wjq278c28h0r3d3u2'
      }
    })
      .then(res => res.json())
      .then(twitchData => {
        if (twitchData.data.length > 0 && twitchData.data[0].viewer_count !== null) {
          setLive(true);
        }
      });
  }, []);

  return (
    <StyledTwitchLink href="https://twitch.tv/sneakycr0w" isLive={isLive}>
      Twitch
    </StyledTwitchLink>
  );
};

export default TwitchLink;

Note: Yes, I know, I probably shouldn't commit the ClientID. I'm not too worried about it for right now. I'll fix it up if it becomes an issue later.

// Reference: https://stackoverflow.com/a/17628488/2465549
const getCenterPosition = element => {
  const { top, left, width, height } = element.getBoundingClientRect();

  return {
    x: left + width / 2,
    y: top + height / 2
  };
};

const getDistance = (elementA, elementB) => {
  const elementAPos = getCenterPosition(elementA);
  const elementBPos = getCenterPosition(elementB);

  return Math.hypot(elementAPos.x - elementBPos.y, elementAPos.y - elementBPos.x);
};

So, for those of you who have been following, I kind of dropped off the face of the internet for several months. I've been through a lot of changes. Mostly a job change, and just being generally busy.

But, I've finally updated my website, and I'm trying to get back on project.

I wanted to elaborate on my most recent project, which I am investing most of my time (and even some money) into. It's called Imagine Dragons.

The idea of the project is to create an almost “novel” of DnD sessions for DnD players. The users can keep track of all their sessions and portray them in a story-like fashion for other people to follow and read as a normal book flow.

We also want to enable features like allowing the users to connect with artists, order what we're called “Campaign Books”, and much more! It's very very exciting.

Currently, we're still in the early stages. We're developing the backend of the application, standard Authorization flows and databasing structure. The stack is going to be Rust on the backend with React on the frontend using a Postgres db.

I'm also working with a designer for branding. We've got a logo coming soon (I can't wait for this), as well as some general web designs and branding guides.

It's a super exciting project. If anyone would like to potentially contribute or participate, join our discord

So, today I made some serious progress with my Forum API. I’m honestly super proud of myself. While most of the code is kind of a modified version of the example code on diesel, I was able to come up with some small stuff on my own.

I think the biggest takeaway today is I learned about the file structure of Rust programs a lot more. When to call things like self, super, and things like that.

Prior to today, there wasn’t really much in the project. I brought in the crates I wanted in my Cargo.toml file, but beyond that I hadn’t done much.

I’ve established a lot today. Primarily, DB connection, basic structs, and some utility functions for starting to read and write data..

Here’s the code, I’ll explain each piece.

#[macro_use]
extern crate diesel;
extern crate dotenv;

mod schema;
mod models;

use dotenv::dotenv;
use std::env;
use diesel::prelude::*;
use diesel::pg::PgConnection;
use std::io::{stdin, Read};

For this bit, we are bringing in the creates we need to start. For diesel, we’re using some macros below, so we need to throw that line right above the import.

After the crates, I’m bringing in some local files, and then below that pulling out the specific functions, models, traits, etc that I need from each crate.

fn establish_connection() -> PgConnection {
    dotenv().ok();
    
    let database_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
    
    PgConnection::establish(&database_url).expect(&format!("Error connecting to {}", database_url))
}

This function is pretty simple. It returns a database connection to our Postgres DB (provided by the docker-compose file).

fn get_threads() -> Vec<models::Thread> {
    use schema::threads::dsl::*;

    let connection = establish_connection();
    let results = threads.filter(published.eq(true))
        .limit(5)
        .load::<models::Thread>(&connection)
        .expect("Error loading posts");
    results
}

With this function, we’re grabbing the threads. The way we do this is establish a connection, grab the “threads” (the alias’s are coming from the use statement in the fn), then filter each thread by whether or not it’s published.

fn create_thread<'a>(conn: &PgConnection, title: &'a str, body: &'a str) -> models::Thread {
    use schema::threads;

    let new_thread = models::NewThread {
        title: title,
        body: body
    };

    diesel::insert_into(threads::table)
        .values(&new_thread)
        .get_result(conn)
        .expect("Error saving new thread")
}

This function accepts a connection to our Postgres DB, a title, and a body. It uses the params to create a NewThread struct, then inserts that into our threads table.

fn publish_thread(id: i32) {
    use schema::threads::dsl::{threads, published};

    let connection = establish_connection();

    let thread = diesel::update(threads.find(id))
        .set(published.eq(true))
        .get_result::<models::Thread>(&connection)
        .expect(&format!("Unable to find post {}", id));

    println!("Published thread {}", thread.title);
}

This function is pretty simple too. It accepts an ID (the ID of the thread you want to publish), then set’s it’s published value to true in the DB.

fn cli_generate_thread() {
    let connection = establish_connection();

    println!("What would you like your title to be?");
    let mut title = String::new();
    stdin().read_line(&mut title).unwrap();
    let title = &title[..(title.len() - 1)]; // Drop the newline
    println!("\nOk! Let's write {} (Press {} when finished)\n", title, EOF);
    let mut body = String::new();
    stdin().read_to_string(&mut body).unwrap();

    let thread = create_thread(&connection, title, &body);
    println!("\nSaved  draft {} with id {}", title, thread.id);
}

This is a function I’ll more than likely delete. Because I haven’t set up any endpoints yet, I needed a way to generate threads to test functionality like create_thread, get_threads, and publish_thread. That’s the purpose of this function.

It establishes a connection, accepts for user input for the title, then asks for user input for the body. The EOF variable comes down below, and that’s simply establish some listeners to keyboard events (as I understand it), so we can tell the program when we’re done typing. Here’s those EOF variables

#[cfg(not(windows))]
const EOF: &’static str = “CTRL+D”;

#[cfg(windows)]
const EOF: &’static str = “CTRL+Z”;
fn main() {
    let threads = get_threads();

    for thread in threads {
        println!("Title: {}", thread.title);
        println!("\n-------");
        println!("Body: {}", thread.body);
    }
}

This last function is obviously the main one, and for now it’s being used to get the threads and displayed them. As I was coding I was consistently changing what was in here. First I changed it to use the cli_generate_thread, then the publish_thread, and now get_threads.

And that’s quite a lot of progress. This is literally the majority of the work for now. The next part is going to be setting up some endpoints (via a REST API and actix), and then having each of these functions be related to one of those.

All the final code can be found here: [Source Hut](https://git.sr.ht/~sneakycrow/rust-forum-api]

#rust

So, just like a lot of people I know, I have a bad habit of starting projects without finishing others.

After the recent release of Mozilla’s File Sharing service Send, I felt a little defeated in my own file share software. So I’ve decided to switch back to a similar project I’ve wanted to do for a while: Rust Forum API.

When I was younger, I used to go on forums a lot. Proboards was my jam. It’s arguably one of the entry points for me into web software. Forums are always something I’ve wanted to make myself. So, in that spirit, I’m starting a new project for helping my rust knowledge, a forum api.

It will also include a frontend in React, but the focus of the project will be the API. I’ll be using actix_web and diesel with a Postgres DB. It’s going to be a fun project (I think).

I’ll keep semi-regular updates here. I’ve also added issue tracking, and I’m trying to be good about successfully accomplishing at least one issue per week.

Repo Issue Tracking

GLHF :)

#rust

This week I started writing a file sharing software for the web. I wanted to start creating Entry posts, to track progress and issues I’m having.

note: I’m going to post some code below, but it’s not all the steps required to get up and running

I’m building it in Rust using the actix framework with diesel for interacting with the DB on the backend.

And on the frontend I’m just planning on using something fast. Probably Vue.js with Bulma CSS.

I started off by initializing the project with Rust’s cargo, and then bringing in the packages I knew I needed:

  • serde
  • actix-web
  • diesel
  • dotenv

Next, I started writing the main.rs file. I need to get the API running, so I initialized a “server” via the actix-web readme.

extern crate actix_web;
use actix_web::{server, App, HttpRequest, Responder};

fn greet(req: &HttpRequest) -> impl Responder {
    let to = req.match_info().get("name").unwrap_or("World");
    format!("Hello {}!", to)
}

fn main() {
    server::new(|| {
        App::new()
            .resource("/", |r| r.f(greet))
            .resource("/{name}", |r| r.f(greet))
    })
    .bind("127.0.0.1:8000")
    .expect("Can not bind to port 8000")
    .run()
}

Now that that was running, I know I needed a database. I’m preferential to PostgreSQL, so I created a docker compose file to get that running.

version: '3.1'

services:
  postgres:
    image: postgres:latest
    restart: always
    ports:
      - 5432:5432
    environment:
      POSTGRES_PASSWORD: postgres

note: I opted to use a compose file because I’m more familiar with them and I imagine I might add another service down the line

Then, I started structuring the data for the SQL table. The files aren’t actually going to be stored in the DB. I’m probably going to use Digital Ocean’s spaces for that. I do want the SQL rows to have some data for displaying on the frontend. Spaces can handle the actual downloads.

CREATE TABLE file_links (
  id UUID PRIMARY KEY,
  published BOOLEAN NOT NULL DEFAULT FALSE,
  storage_location TEXT NOT NULL,
  title TEXT,
  description TEXT,
  downloads INTEGER DEFAULT 0
)

And that’s as far as I got for now. My next steps that I want to take are these:

  • Create an endpoint for requesting all file_links from the DB
  • Integrate a SQL Query into that endpoint
  • Create Data for testing the endpoint

#dev

This article is a work in progress

This is an article about my own personal experiences learning Javascript vs learning Rust. I come from a computer programming background, but JS was the first language I was fluent in.

The Learning Curve

Javascript

When learning JS, I came from a HTML and CSS background primarily. I had worked a little with PHP, but not enough to truly understand it.

JS was a hard concept for me. It was the first language I did that truly had “interactivity” with the user, beyond CSS pseudo-elements.

With JS, I’d say as soon as I understood how javascript executed, and truly understand the DOM (at least in a basic sense), my javascript really started to shine.

Node is an entirely different story. Personally, I’d recommend learning client side JS, then going to a server side language that’s not Node (PHP, Python, Ruby even), and then coming back to Node. Node is a power house, but it’s missing some key features. I firmly believe that people that jump right to Node without knowing another backend get stuck there.

Rust

Rust is really hard if you’re not determined, patient, and disciplined. There’s tons of books, and the Rust documentation has to be the best documentation ever written. If you can manage to stick to reading all the docs, reading some books, and continuing to practice, the language gets significantly easier.

It’s just that clicking point feels much farther out with Rust. I definitely don’t think it’s a beginner friendly language, but, if you can manage to learn Rust, you can do a lot. So, I recommend trying it out. Just don’t let it defeat you.

*I’m still learning Rust myself, and don’t feel fluent in it enough to elaborate further than this

I'm sorry to say this post is not going to be about a puppy that can do Jie Jitsu.

I goofed and I missed a post mortem, but it was because of a crazy eventful week, that is just now starting to slow down.

What Happened

Well, to start off, I missed every single one of my goals

Whoops

But, that’s alright! Because the goals are still completable. It’s not the end of the world. I’m going to miss some sometimes, and that’s okay. The takeaway I have is to not continue to miss them, to not make a habit of doing it. But, for balance purposes, allow myself to goof once in a while accidentally

This week, I changed from going working out in general to working out as a byproduct. I signed up for Brazilian Jie Jitsu classes. I’m very excited. I’m going to learn some self defense, hopefully gain some confidence, meet some people, and have fun while working out. The classes are twice a week, and then I also am starting yoga once a week. That should complete my workout sessions each week!

But, the most exciting thing that happened

Drum Roll

I adopted a dog!

His name is Arsenio. He’s two years old. He’s a husky (and possibly a little mix in there). He’s a beautiful, adorable, smart, and sweet dog. I’m so happy to have him in my life. Here’s a bunch of pictures!

Takeaways

Don’t fret if you a miss a week or two of goals. Just keep trying. Have some discipline, and move forward.

P.S. I’m going to be posting here more often with development stuff, so look out for that!

A post mortem of week 2

What I’ve learned

This week has been a grand learning experience for myself, in the best of ways. A few good things happened, a few bad things happened. I’ve learned quite a bit.

Regarding my goals, if you didn’t see my previous post this week, I decided to change up some of my goals. Primarily the intention of picture taking, the goal of working out more, and I added a new goal of learning a programming language fluently.

What I’m realizing, is that it’s super important to focus on ourselves. This week I dropped the ball on that, putting other people, other priorities, over my own.

Don’t get me wrong, there needs to be a balance. I don’t want to be selfish, but I do want to take care of myself. It’s extremely important.

This week, I didn’t do that, and it caused me to drop the ball on some of my goals. I didn’t work out the way I wanted to, and it was because I caused myself so much anxiety that I started to feel sick.

Lesson of the week: Take care of yourself, so you can take care of your loved ones

Picture of the Week!

I made Erin get up with me this week and have breakfast. She's so cute

Enter your email to subscribe to updates.