Giter VIP home page Giter VIP logo

ng9-literary-clock's Introduction

LiteraryClock

Background

I originally found this article describing how Jaap Meijers made a literary clock using an old Amazon Kindle and a quote list generated by The Guardian. I used the same quote list and created a rather simple web page that also worked as a literary clock. (This is not the original version as I wasn't using GitHub when I first coded a working solution.)

As many developers will tell you, I revisited the code much later and found that I didn't like it. The original version had everything as one giant function. It was poorly organized. It just made me cringe, as all old code can and often does.

I decided to create an updated version of the application. My days are currently (as of this writing) spent working with Angular applications. Being the most familiar to me, I went with that framework. The current iteration is built using Angular 9, although ng10 has recently been released.

After building the application, I was not satisfied that I had a multiple gaping holes in the dataset. I did some more searching and found a very functional, very complete literary clock at LiteraryClock.com. I reached out to the site's creator, Justin Whalley, who gladly handed over his pains-takingly curated dataset. So a MASSIVE thank you to Justin!

Code Break-down

Getting the time

This actually breaks into two problems, as the dataset I have is written using a 24hr time model and I want to display as 12hr time model. Either way, I need the current time. So I use the ES5 Date method.

// clock.component.ts #SetTime()
    const now = new Date();

Now that I have the current time stamp, I need to get the hours, the minutes, and also decide if I need to display 'AM' or 'PM'.

    let h = now.getHours();
    const m = now.getMinutes();
    const i = now.getHours() > 11 ? 'PM' : 'AM';

You'll notice that I use let for h instead of const. This is on purpose as I have other transformations to also do on the hours.

Handling 24-to-12-hour clock conversion

Since any time after 12:59 PM has an hour value larger than 12, I need to account for that value being non-conforming with my 12hr clock. So I account for it, using a method that returns a boolean.

    if (this.AdjustTime24to12(h)) {
        h = h - 12;
    }

I could have used a ternary operator, but this is a little more readable, as it clearly indicates what I'm doing and why.

    private AdjustTime24to12(hours: number): boolean {
        return hours > 12 ? true : false;
    }

Creating the time search parameter

I also need a valid 24hr time value to compare with the dataset, which has leading zeros. I first tackled the very simple task fo padding the time.

    // in #SetTime()
    this.time.next(`${h}:${this.Padding(m)} ${i}`);

    // Method to pad zeros
    private Padding(time: number): string {
        return time < 10 ? `0${time}` : time.toString();
    }

It's ok to return a string, because I'm looking to match the value against a JSON value. The time value being set is an RxJs Behavior Subject.

Now, for the 24hr time. I didn't want to generate a new time, even though this code will run fast enough that no one would notice. I pass the previously generated now value into a new method to create my dataset search parameter.

    private SetMilTime(now: Date): void {
        const h = now.getHours();
        const m = now.getMinutes();
        this.milTime.next(`${this.Padding(h)}:${this.Padding(m)}`);
    }

The milTime is also a Behavior Subject. The value is emitted here.

Getting the quote

Now, I need to create a function that will get the quote from the dataset.

    private GetQuote(): Quote {
        return this.quotes.filter(q => q.time === this.milTime.value)[0] || null;
    }

The quotes variable is the imported dataset. It's an array of quote objects. This is the interface:

export interface Quote {
    time: string;
    quote: string;
    source: string;
    author: string;
}

The filter method returns an array, even if no elements match the search parameter. I found it easier to return a single quote value than to add more logic elsewhere. Also, I added the || or null statement to deal with the possibility that one of the 1440 quotes could magically disappear.

Bringing it all together

setInterval is appropriate for repeating code based on a timer. But where would I invoke the method? I chose to use the Angular ngOnInit Lifecycle hook. This piece of code only gets called once as the page loads. ngOnChanges gets called first, but it made no difference in execution. Since no user-initiated changes were ever going to happen, this seemed like the best hook to use.

    ngOnInit(): void {
        setInterval(() => {
        this.SetMilTime(this.SetTime());
        this.quote = this.GetQuote();
        }, 1000);
    }

This method does two things, in sequence: sets the time (and search param) and gets the quote from the dataset.

The code is executed each second. I could tweak this to ensure that this application won't become a resources hog, but for the purposes of such a simple app, it's fine.

The rest of the code is rather straight forward. The HTML is a mere 17-ish lines for the component.

<main>
  <section class="time">
    {{ time | async }}
  </section>
  <section>
    <article class="quote green">
      <p> {{ quote?.quote }}</p>
    </article>
  </section>
  <section class="purple">
    <article class="source">
      {{ quote?.source}}
    </article>
    <article class="author">
      {{ quote?.author}}
    </article>
  </section>
</main>

I subscribe to the time behavior subject using the async pipe. This ensures the subscribe() and unsubscribe() methods will be handled automatically. I used the null-safe (Elvis) operator on the quote properties. As the page loads, there actually isn't a valid quote for a few hundred milliseconds. The page will load, but it throws an error into the console.

Styling

I decided to go with an artistic flare for this clock. I am a fan of Impressionist paintings, with Matisse being my favorite. Water Lilies is one of my favorite paintings. I used it for the background. I used ColorZilla's Chrome Extension to sample colors from the painting and used those colors (with a 40% opacity) as the background colors for the text. I like the Special Elite font from Google Fonts. The remaining CSS exercise is nothing special.

Improvements

Ordinarily, I would have crammed all of the logic for this application into a Service. My display component would have been 'dumb,' merely subscribing to the value and displaying the results. I could possibly revisit this issue in the coming weeks. For now, this is the MVP of a literary clock.

Contributions

I am open to improvements on the code and will gladly accept any additional quotes for the dataset. Please fork the repo, code your heart out, and submit a pull request.

Bugs

Please submit any bugs you find. Since the application is only supposed to display a quote, the expected behavior is a given. The observed behavior is not. So be verbose if you have to.

ng9-literary-clock's People

Contributors

chadesmith42 avatar dependabot[bot] avatar

Watchers

James Cloos avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.