freitzzz / simpsons-shorts

A desktop app to watch Simpsons shorts while your monolith app compiles. Made to learn Compose Multiplatform!

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

simpsons-shorts

A desktop app to watch Simpsons shorts while your monolith app compiles. Made to learn Compose Multiplatform!

app icon


How?

The app was developed and built with Compose Multiplatform, a framework to build applications for multiple platforms, without having to rewrite the same code multiple times. It's very similar to Flutter in terms of architecture, declarative programming model and API.

Architecture

In terms of project structure, the application has been segregated in several different layers:

  • core, responsible for providing the app foundations like DI and state management.
  • data, responsible for providing ways to communicate with external services and data sources.
  • logging, responsible for logging logic.
  • presentation, responsible for everything UI related such as re-usable compose blocks.
  • state, responsible for domain state management logic.

And the diagram below describes how these layers communicate with each other:

uml diagram describing how the app decomposes in several layers

The shorts URLs are stored in a single JSON file that is hosted on GitHub, so they can be queried freely and in a fast manner.

To play the short, VLC media player is required to exist at runtime. I couldn't find a better way to do this, and the compose block for the video player was adapted from Kotlin Multiplatform desktop example.

Endpoints

If you are building the app from source, you can try different endpoints for fetching the shorts URLs using Cinderela networking client:

  • /small-databases/simpson-shorts.json will fetch URLs of shorts hosted on YouTube.
  • /small-databases/simpsons-shorts-hub.json will fetch URLs of shorts hosted on GitHub. These shorts will play faster.

What is a Reactor?

Reactor is an event stream like reaction engine that propagates events into 0 or more states (1 Event > * State). It's used to operate as middleman between UI and domain logic, being states new UI updates. The provided API is pretty straight forwarded:

First, you will need to implement a Reactor for your domain use case (in the case of the app, a reactor for playing shorts).

class AuthReactor : Reactor<AuthEvent, AuthState>(NotAuthenticated())

Secondly, you will need to think about events and states. Events are actions you want to take in your use case (Next short) and states are updates originated from these events (fetched shorts urls).

interface AuthEvent
data class AuthenticationStarted(val credentials: Credentials) : AuthEvent
...

interface AuthState
class AuthenticationInProgress : AuthState
class AuthenticationSuccessful : AuthState
class AuthenticationFailure : AuthState

Having defined your API, you can start implementing the event to state logic for your domain use cases. For this, you will need to call the on<Event> API inside the init block:

init {
    on<AuthenticationStarted> {
    val credentials = Credentials(it.username, it.password)
    val authenticated = AuthenticationService.login(credentials)

    if (authenticated) {
        emit(AuthenticationSuccessful())
    } else {
        emit(AuthenticationFailure())
    }
}

Finally, when users take actions in the app, dispatch events in the reactor using add method. If all goes well, you can subscribe to the state using the subscribe compose call:

val reactor = AuthReactor()
reactor.add(AuthenticationStarted())

val state by reactor.subscribe()

Note: if you have used Flutter before, Reactor is basically the same as a Bloc. Many thanks to the bloc team and @ptrbrynt for the inspiration.

Dependencies

The application attempts to be bloat free and use as many available APIs as possible. For HTTP requests, a wrapper around Java URL was created to replace fat dependencies such as RetroFit.

For JSON parsing I didn't want to implement a small and error-prone parser, so decided to stick with kotlinx-serialization-json (without intermediate classes generation) instead.

For logging, I'm using my own library (lumberkodee) that provides a customizable and friendly API for logging anywhere in the app with top-level calls.

The fatter dependency is for interoping with VLC: uk.co.caprica:vlcj.

Requirements

To run the app, you will need to have VLC installed on your machine (this applies to both Windows and Linux/macOS). To build the app, you will need a JDK >= 15 (alternatively you can use a JDK >= 11 to run the app via IDE, but you can't distribute/ship it). I built the app using version 17.0.7-amzn (Amazon Corretto).

Building and Distribution

To build the app for local installation: ./gradlew createDistributable

To distribute the app: ./gradlew packageDistributionForCurrentOS

To simply run the app: ./gradlew run

Demo

2023-10-29.12-32-21.mp4

About

A desktop app to watch Simpsons shorts while your monolith app compiles. Made to learn Compose Multiplatform!

License:MIT License


Languages

Language:Kotlin 96.5%Language:Java 3.5%