Skip to main content

Upvote a new feature

· 4 min read
Founder

We added a new functionality, where users can upvote or add new feature requests.

parsing

I have some ideas of my own what to work on, but time is a constraint.

Hence, I would like users to be able to openly suggest what functionality they would appreciate (and other users to possibly upvote a feature or comment on a suggestion).

To be able to suggest/upvote/comment, user needs to be signed in, otherwise he can just view.

Implementation

Evaluating Third-Party Options

At first I wanted to use a 3rd party service. I had a look at canny.io. If you look closely, a design of a feature dashboard is not very far from canny.

I did not like how users had to be logged in separately in a 3rd party service. Although they do offer a possibility to identify a user, it's a plain javascript script, which does not play great with angular. And there are some unexpected states. What if I go to vote as anonymous user (create account with canny) and then create account with cryptoquant. And last, lowest paid tier of canny.io is $70/month, which I believe is too much of an ask for a user feedback feature.

Building a Custom Solution

So as a right builder, I decided to build it. How hard can it be, right?

Permission Management: RBAC vs. ABAC1

If you don't know what these mean, don't worry, neither did I. Only later did I realize I would need to boost up my who is allowed to do what logic.

User level is far from sufficient criteria for allowing user to perform actions. Only user who wrote given suggestion should be allowed to edit it (or delete it), unless we are an admin.

But anyone signed in should be able to upvote a feature (unless he already did so), in which case he will remove his upvote. Hence, you need to access a resource as well (user level is not sufficient) to be able to evaluate a rule. And this is what Attribute-Based Access Control means.

An example how this can look in typescript is below.

export type ResourcePolicy = {
resource: string;
action: string;
allowedRoles: UserRole[];
check?: (user: User, resource: any) => boolean;
};

export const POLICIES: ResourcePolicy[] = [
{
resource: "featureRequest",
action: "create",
allowedRoles: ["notpaying", "paying", "admin"],
},
{
resource: "featureRequest",
action: "update",
allowedRoles: ["notpaying", "paying", "admin"],
check: (user: User, resource: FeatureRequest) =>
isNotNil(user) &&
(user.role === "admin" || user.userId === resource.userId),
}];

Then we have a function to evaluate if given user/resource combination is allowed access, like so function checkResource<T>(resource: string, action: string, resourceObj?: T): boolean {}.

Hence we just need to define in template what we are checking right now as so checkResource("featureRequest", "update", item) in a template.

Not to make this too long, there are more parts to this, such as optimistic updates (which i did not implement), or how the user sees an up-to-date data after liking a comment or posting a feature request.

This is where 2 distinct features shine:

  • F# with messaging based (on pattern-matching) approach
  • Angular with rxjs streams (and services)

I am using these two features extensively and I believe both of them deserve separate article/attention. But in short, combination of these two simplifies (almost) real-time updates of user state. There is only a single (latest) state which gets updated automatically in frontend (because backend pushes it), so no ad-hoc logic is required.

Conclusion

This new feature request system should help me prioritize development based on your actual needs. I'm excited to see what features you'll suggest and which ones gain the most traction. For now I have added some features I believe can be useful and I would be excited to work on.

Feel free to head over to the feature requests page to add your suggestions or upvote existing ones. Your input is valuable and will directly influence the future development of cryptoquant.

As always, I'm continuously improving both the platform's functionality and architecture. The combination of F# backend and Angular frontend has proven to be a powerful approach for building responsive, real-time features like this one. I look forward to sharing more technical insights about these implementations in future articles.

Footnotes

  1. Role-Based Access Control .vs Attribute-Based Access Control, for a more lengthy example see this