Skip to content

Latest commit

 

History

History
102 lines (61 loc) · 9.06 KB

Style-Guide.md

File metadata and controls

102 lines (61 loc) · 9.06 KB

Style Guide

Please read all the sections below before writing a single line of code.

JavaScript Style Guide

While our build system should automatically detect most deviations from JS Standard, it might not catch all (especially in .vue files).

If you notice any files not properly linted by standard, this means there's a bug, and you're welcome to help fix it!

It is still on you to ensure your code conforms to the standard spec, whether or not the linter catches everything.

Vue.js Style Guide

Since this is a Vue.js project, any pull requests must follow Priority A rules mentioned in the Vue.js Style Guide, and should follow the Priority B rules. Please take the time to read at least those two sections.

CSS/SASS Style Guide

For styling, we use Bulma.

  • Everything that can be solved by using Bulma’s classes should be solved with them
  • Theme overwrites that affect the whole application - for elements that could be found at the Bulma docs - should go to /frontend/simple/sass and be written in SCSS
  • Component specific styles should go to the component’s <style> tag, be written in SCSS, and be scoped to the component. Try to write as little of this as possible.

Use correct Bulma documentation

Bulma sometimes changes significantly. Therefore it's important:

  1. To access the correct documentation for the version of Bulma that we're using. (Check package.json, and then visit the version-specific docs, the earliest available of which is 0.4.4)
  2. Never update Bulma "just because"! Always create an issue to update bulma first, and then create a single unique pull-request just for that issue. You will have to verify and fix any UI-differences that occur. Sometimes Bulma will remove or change CSS selector class names, etc. You must identify and fix all differences.

Group Income Data Model Rules

Group Income adds additional rules for how to write .vue components.

These rules come out of many lessons learned. All new pull requests must follow these rules. The sections below first describe the Group Income data model, and then describe the actual rules in "How to organize data ()".

Note: some old code unfortunately does not follow these rules, and we encourage contributors to help clean it up so that it does.

Overview

If you're familiar with Ethereum smart contracts, you'll be somewhat familiar with how Group Income structures its data, however, many of the terms you're familiar with will have slightly different meanings and implementations in Group Income.

For example, in Group Income, data is synchronized between users through "blockchains" and "smart contracts", but these chains and contracts have more in common with git than they do with Ethereum.

Here are the primary differences:

  • In Group Income, "blockchains" are smart contracts. Each contract is its own private chain of events called a contract chain.
  • Each contract is identified by the hash of the transaction that created it, which is also the start of its contract chain.
  • All contract chains created by Group Income are private and can only be accessed to by those authorized to. For example, a GroupContract can only be read and written to by current group members.
  • Data is synchronized between clients through a central node. Each group member has the ability to turn their client into this coordinating node, but every group uses a single always-online node to transmit and sync changes to logs. There is no consensus process beyond this node because no consensus process is needed since everyone already trusts each other.

Since every contract represents a log of events, you can run through the actions/events in the log to build up a state for the contract object represented by that event log.

In Group Income, the client runs chains through Vuex to build up the application state via the handleEvent() function in frontend/simple/state.js.

There is a direct 1-to-1 mapping between the contract event logs, and Vuex mutations and actions. The contracts are defined in shared/events.js, and their Vuex mappings are defined in frontend/simple/js/events.js.

Persistent State vs. Ephemeral State

There are two primary kinds of state in Group Income:

  • Persistent state refers primarily to the Vuex state that gets serialized to disk, and in general it also refers to everything that creates that state. This includes the contracts and the transactions that create them. In other words, persistent state is the state that is at some point sent over the Internet, and it is the data that persists between application launches.
    • When the application starts, it loads data from disk (via localStorage) into the Vuex store.
    • The state for every contract we're subscribed to exists as a unique module in Vuex, the key for which is the hash of the first transaction that created the contract.
    • Almost everything in the vuex state is serialized to disk to the local "database" (i.e. localStorage) so that it's available the next time the application starts.
    • Events for each contract we're subscribed to pass through the handleEvent() function in state.js. Whenever we (the client) send out a transaction that modifies the group state, we first send it to the server, and only update the Vuex state when the server echoes the transaction back to us (and then it gets processed like all the others by handleEvent()).
  • Ephemeral state refers to everything else. Much of the data in Group Income does not need to be saved to disk or sent over the Internet. For example, none of the metadata related to validating forms needs to be saved to disk or kept around between application launches. And although some state might exist in contract chains (for example, the URL to a profile picture), that does not mean Group Income will persist every contract a user interacts with.
    • The latestContractState() function in state.js should be used in situations where data from a contract chain is needed momentarily, but might not need to be saved forever. It will fetch all of the events in a contract and use them to build up a final state for that contract. For example, latestContractState() is used in Join.vue to show avatars for group members in a group the user is considering joining. Since the user might decide not to join a group, we do not subscribe to the IdentityContracts of any of those users and do not persist any of that information (until the user actually joins the group).
    • All ephemeral state should be stored in a Vue component's data () section. There is almost no exception to this.

How to organize data ()

Having components keep track of their own state is considered poor practice. It makes the application difficult to reason about and maintain.

Therefore, components should be "purely functional" in the sense that virtually all of their state is derived from the vuex store, computed properties based on that store, or props that are passed in to the component from a parent component.

There are three exceptions, and all of them have to do with different types of ephemeral state (see above).

The object returned by data () should have only these keys:

  1. form — form validation metadata for vuelidate.
  2. config — some Vue.js components require bindings to config data (for example, the sliderConfig for <vue-slider> in TimeTravel.vue and steps for components/StepAssistant.js in CreateGroup.vue).
  3. ephemeral — any data we might not care to save beyond the life of the view (unless some other action is performed) should be placed here. There shouldn't be much that fits in this category (as, for example, most error handling stuff should be handled by form and in the <template> section). However, it might be useful to cache some data temporarily (like a profile picture or URL) here.

Remember: if you can use a computed property based on the vuex store, it means you probably should.

SBP

Virtually everything in this project is going to be converted to SBP ("selector-based programming", see shared/sbp.js).

Details about SBP will be written in a blog post soon. In the meantime, you are encouraged to adopt this paradigm wherever possible for your own code.

Search the project for sbp( for examples, and speak with @taoeffect about it before diving in (at least until the docs for SBP are still waiting to be written).

SBP is the greatest thing to happen to programming since computers were invented! :D->-<