skydread1 / flybot.sg Goto Github PK
View Code? Open in Web Editor NEWOfficial Website of Flybot Pte Ltd
Home Page: https://www.flybot.sg
Official Website of Flybot Pte Ltd
Home Page: https://www.flybot.sg
There is no User in the backend api.
/users
to the api pullable-dataThe Helmet
component we have is quite empty and should have contain more info.
Also all pages have the same meta which is not advised when it comes to SEO.
Helmet
component to have a custom title
, description
and keywords
for each page.Generator for Google+/Facebook/Twitter:
Validator:
Having a logo would make the website more vivid.
Use one of the logo provided by the team at 4AVCT
We just created a company page on LinkedIn and we can add it now to the website.
When the user (in editor mode) wants to update the page config (such as the order of post for instance), we don't want to show the individual post edit configs at the same time).
:post/mode
and a :page/mode
to distinguish the page settings from the post settings.Edit Page
button to show/hide the page config option in editor mode as well.We do not have unit or regression tests for the frontend.
There is no page introducing Flybot company and the team
We need to add the link to magic
, nostrand
, golden island
, clojure
and unity
.
We can share the same Malli schemas for
The main difference between validation schema and pull pattern schema is that pull pattern schemas have all keys optional as we do not want to force the client to require any fields.
However, for validation (form inputs for frontend, request params for backend), we often need the client to provide some mandatory fields.
validation
schemamalli.util/optional-keys
function to make all the keys optional for the pattern
(query) schemaThe h2 tags do not follow the theme of the website in the apply
page.
The post or by default ordered by creation date.
We cannot sort them by last edited date or specify individual post positions.
We don't have the date infos display in the UI.
If though our content is relatively small, we should still export at least common parts such as the header
and footer
.
It is better for reusability and scaling if we need in the future.
Break down the page in different React components.
We do not have the favicon setup on our website.
Use the Flybot logo as favicon
Some shadow borders are not showing at the bottom of the divs.
Add relative
and the z-index
to fix this issue
As for now in the frontend, the user
, page
and post
mode are at the same level such as:
;; initial db
{:app/current-view (rfe/push-state :flybot/home)
:app/theme local-store-theme
:user/mode :reader
:page/mode :read ;;wrong scope
:post/mode :read ;; wrong scope
:nav/navbar-open? false}
This is conceptually wrong as each page should have its mode and each post should have its mode.
This has for consequence some overcomplexity in the logic at the post level which could be easily avoided via individual post modes and individual page modes.
:page/mode
to page level and :post/mode
to post levelThere is no readme describing the stack used and how to add content for non-developers via the markdown files.
The current implementation execute the side effect when a pattern contains a :with
option in one of its keys.
This cause no problem if there is only one key using the :with
option such as:
;; the value of :a being a function causing a transaction
{(list :a :with [a1]) '?}
However, if there are 2 keys with function causing transactions, the order cannot be guaranteed, such as:
;; values of :a and :b cause transactions
;; no guaranty of order
{(list :a :with [a1]) '?
(list :b :with [b1]) '?}
As for now, we can only create a post from scratch but not modify an existing one.
Also the new post can only be added to the blog
page.
As for now, we use mount to create/populate/close the DB and start/stop the server.
Mount lets each namespace declare global variables to be added to the life-cycle via defstate
.
We prefer the approach where we define a unique system map which can handle app life-cycle management while retaining the feature of a simple Clojure map.
Use robertluo/fun-map to manage the app life-cycle.
The following as been removed by accident in the previous PR:
<meta name="viewport" content="width=device-width, initial-scale=1" />
So the display is not correct on mobile
Add the line back to index.html
Same as for this github issue, it is convenient to have a way to preview the post before submitting it.
There is no way for a non-developer to write articles.
To do so, we need a blog
page where all the posts that were written in markdown can be displayed.
Each article in the page can link to a dedicated dynamic page with the content.
We can use mpcarolin/markdown-to-hiccup to convert the markdown from the articles to hiccup to display it on the cljs page.
metosin/reitit will be useful for routing the blog posts
We are looking for either intern or full-time and we don't specify it.
Add it to the job description.
As for now, we cannot choose one image for light mode and one image for dark mode while writing a post via the UI.
We should be able to specify:
beside-image
. (Add field to UI and DB)purge
allow to deploy smaller css in production.
Add the purge config in tailwinbg.config,js
We cannot delete a post as for now.
As for now, we perform the http request directly in the handler (which works but is not pure).
reg-event-fx
.We need to generate a uberjar
for deployment.
uber
task in build.clj
deploy
tasks to generate the js bundle (client) and then generate the uberjar (server)We just want a basic static website as our content is small.
We first though of letting netlify
do the building as it supports Clojure CLI command such as aliases.
However netlify
does not support clj -T
of build.js
which is a problem for us.
Ideally, we want to be host agnostic and not expose the js bundle in the github repo.
Lots of people prefer dark version of website to not hurt their eyes too much especially when it's late.
Lots of people actually have their device setting defaulting to dark mode.
We did not optimise the PNG files before upload and that is not good for bandwidth consumption and overall speed of the website.
Using a site like tinypng to compress the images and update the repo.
Reloading the main page /
is fine but reloading a subpage (for instance /about
) causes a page not found.
Hosting a React application on the Netlify platform often runs into an "Error 404". This is because an app built with Reactjs is a SPA (Single Page Application). Routing is done on the application and no HTTP calls are made to fetch pages server-side.
The fix is to add a _redirects
file in the public
.
This tells Netlify to route all redirects to the index.html which is the root entry of our application.
As for now, anybody can create a post.
We can provide a ring handler in the figwheel-main.edn
.
This allow us to have a server running for the backend while preserving the hot cljs reloading for front-end development.
datomic-free
(or datomic dev-local
) deps in deps.edn
The client request params are outdated. We do not use the concept of operations anymore.
Use the pull pattern leveraging the :with
option in the request params.
We are often interested in a subset of the data returns by the server from a given request.
This is made intuitive via the library lasagna-pull.
In the client request' body, a pull pattern
is provided to only pull a subset of what the server logic could have computed. The server will then perform the corresponding logic and return only the data requested by the client.
In our case, we can create a post like this:
;; front-end (.cljs)
{:http-xhrio {:method :post
:uri "/all"
:params {(list :op/create-post :with [post])
{:post/id '?
:post/page '?
:post/css-class '?
:post/creation-date '?
:post/last-edit-date '?
:post/show-dates? '?
:post/md-content '?
:post/image-beside {:image/src '?
:image/src-dark '?
:image/alt '?}}}
:format (edn-request-format {:keywords? true})
:response-format (edn-response-format {:keywords? true})
:on-success [:fx.http/send-post-success current-page]
:on-failure [:fx.http/failure]}}
You can see that the pattern allows us
The :with
option in the key (list :op/create-post :with [post])
allows us to provide some params to the value. In our case, we provide a post
to the value of the key :op/create-post
which is a function.
We can use schema for data validation: the server can provide a schema with the data allowed to be pulled by the client. We can have different schemas for different requests. The schemas could even be used for front-end validation as well so we can put them in a .cljc file such as:
;; front/back end validation (.cljc)
(def post-schema
[:map {:closed true}
[:post/id :string]
[:post/page :keyword]
[:post/css-class {:optional true} :string]
[:post/creation-date inst?]
[:post/last-edit-date {:optional true} inst?]
[:post/show-dates? {:optional true} :boolean]
[:post/md-content :string]
[:post/image-beside
{:optional true}
[:map
[:image/src :string]
[:image/src-dark :string]
[:image/alt :string]]]])
(def ops-schema
"Schema of all the operations that can be performed in the server."
[:map
[:op/create-post {:optional true} post-schema]
...])
On the server-side, we have the run/pull
function accepting the pattern
, a malli schema
and the data
to pull the info from.
The pattern
comes from the request, the schema
is defined by the server (can be viewed as a protocol to be respected by the client) and the data
is what results from the backend logic (after eventual side effect):
;; backend (.clj)
(defn ops-fn
[sys]
{:op/create-post (fn [params] (:response (create-post (assoc sys :params params))))
...})
;; call to the pull function in the request handler
(first (pull/run body-params v/ops-schema (op/ops-fn sys)))
In our example, the post
provided in the pattern for the key :op/create-post
is given to the create-post
function which will update the db and send a response respecting the post-schema
shown above.
An example of what could be returned by the server is:
{:op/create-post #:post{:id "2bbf635f-1210-4168-9b8a-6f668461ccbe"
:page :home
:css-class "magic"
:creation-date #inst "2022-10-10T03:59:24.492-00:00"
:md-content "some md content"
:image-beside
#:image{:src "assets/binary.svg",
:src-dark "assets/binary-dark-mode.svg"
:alt "Love word written in base 2"}}}
Note that, in opposition to a RESP api, the request type does not matter as we just care about the pattern provided in the body. Same remark for the routes, we do not care as the end-point is implicitly defined in the data shape itself.
As we opt for re-frame, we should avoid local atom and use dispatch/subscribe instead.
Also, there is no way to know if an error occurred and if it is a validation error or a request error.
Now that we have a good basic content/layout, we can deploy the website to a free tier of a server host.
Our website is very small and won't generate heavy traffic at all hence the free tier should suffice.
Netlify is good candidate for hosting for the following reasons:
Vercel
)The localstorage value is not retrieve properly hence defaulting to dark mode.
in flybot.lib.cljs.localstorage
Replace
(if-let [l-storage-theme (keyword (get-item "theme"))]
...)
by
(if-let [l-storage-theme (get-item (keyword "theme"))]
...)
As for now, the client request is expected to have op-name
, op-params
and pattern
such as:
{:op-name :op/add-page
:op-params [page]
:pattern '{:page/name ?}}
This protocol restricts the implementation and do not make full use of the pull stack.
We should use the pull-pattern :with
option in the request and only pass a pattern to the server.
The example above could become:
{(list :something :with [page])
{:page/name '?}}
The :something
needs to be related to actual data and not a server operation
It is backend responsibility to ensure the params given with with
to the value of :something
are allowing us to compute the corresponding logic and to describe side effects to be executed as well.
Just use a pattern as request params and modify the backend to get rid of the operations workflow.
There is no way to write a post in the front-end ad store it in the backend.
create-post
page with a form to write the markdown and some properties.There is no way to validate the config specified at the top of the .md files.
Use metosin/malli for the schema and so data validation.
As a company specialised in Clojure, it makes more sense to have a Clojure implementation of the website
We can use the following stack:
For larger app or just to scale the front-end, some state management is needed to isolate the mutation and render the component properly.
We do not have tests for the reitit
ring-handler.
test-system
to setup a test server and test db with sample datapulled-data
via the different pull-patternsdark mode
should be the default theme if localStorage
is empty
Add the dark mode theme
with dark value in the localStorage
.
In theory, a SPA is fine for our need as we just deliver static content.
However, as part of our work on our open-source lasagna stack (fun-map, pull and remote-pull),
we want to use our website as an example of the good use case of our stack.
So, for a pull api and map value accessing demo to make sense, we need an actual backend.
We need to be able to store the data in a DB.
datomic-free
library with a in-memory DBIn the future, we will need actual data persistence but at this stage, we are just experimenting in the dev environment.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.