* Inspiring Adversary ** raindrop.io ** linkwarden.app ** getpocket.com ** saved.io ** pinterest ** www.pagemarker.io ** getmemex.com ** www.linkpack.io ** larder.io ** notado.app ** www.linkace.org ** bmrk.cc ** bmrks.com ** bookmarkmanager.com * Same Name ** brace.io - www.studioanthonylane.com/Brace-io ** brace.ai - Software to Revolutionize Mortgage Servicing * poc ** subdomain names for tags - set up a catch-all (wildcard) subdomain https://www.namecheap.com/support/knowledgebase/article.aspx/597/2237/how-can-i-set-up-a-catchall-wildcard-subdomain/ - Use CNAME Record CNAME Record - @ - parkingpage.namecheap.com. - 30 min CNAME Record - * - parkingpage.namecheap.com. - 30 min - If value is IP, change CNAME Record to A Record - Can retrieve the whole url with js? ** static files + custom domain + wildcard SSL + CDN *** google storage - Works like CDN but not good as CDN - No support https on custom domain *** firebase - No support wildcard SSL, only support https on subdomains, no more than 20 subdomains! and need to setup one by one! *** google storage + google load balancer + google CDN - Require load balancer to work with google CDN - $18++ per month *** google storage + cloudflare - $5++ per month *** render.com - Too newly startup *** netlify - Limited usage. If exceed, $45++ per month *** google app engine - Need wildcard SSL from 3rd party i.e. from cloudflare $5 per month - No CDN. Platform tries to distribute across its datacenters - Should use standard enviroment for cost saving and scalability *** aws cloudfront + s3 **** s3 - Create a new bucket with public access - Upload files and folders in folder 'build' and make them all public - At bucket properties, enable static website hosting + Index document: index.html + Error document: index.html - access at http://brace-web.s3-website-us-east-1.amazonaws.com/ **** certificate manager - Verify ownership of the domain name + Need to use AWS N.Virginia + At namecheap, host records -> CNAME Record + Name to Host but without domain name i.e _dea0a23bf1e683425e901e82d7c7e816.brace.to. -> _dea0a23bf1e683425e901e82d7c7e816 + Value to Value as is + ref: https://docs.aws.amazon.com/acm/latest/userguide/gs-acm-validate-dns.html, https://stackoverflow.com/questions/51198472/cname-entry-not-working-on-namecheap-using-amazon-certificate-manager **** cloudfront - Origin domain name -> type yourself, from dropdown list is incorrect - Viewer protocal policy -> Redirect HTTP -> HTTPS - might not necessary + Default root object -> index.html + Error pages -> index.html - At namecheap, CNAME Record on * and @ to cloudfront domain name i.e. df3eruy30b9uq.cloudfront.net + it will ended with dot because https://stackexchange.github.io/dnscontrol/why-the-dot - *Invalidating files* + Try to use name versioning instead as more than 1000 requests needs to pay (https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Invalidation.html) **** ref - https://medium.com/@wolovim/deploying-create-react-app-to-s3-or-cloudfront-48dae4ce0af - https://medium.com/dailyjs/a-guide-to-deploying-your-react-app-with-aws-s3-including-https-a-custom-domain-a-cdn-and-58245251f081 - https://benjamincongdon.me/blog/2017/06/13/How-to-Deploy-a-Secure-Static-Site-to-AWS-with-S3-and-CloudFront/#fnref:5 ** web design *** consideration - color - contrast - white space - visual hierarchy - scale - simplicity - consistency - responsiveness *** guideline **** layout, sizing, spacing, margin, padding, line-height ***** The 8-Point Grid with 4-point baseline system - Set grid with 8px width and height - soft grid + If size is divided by 8, margin or padding might not + Use 4pt system? too small? - ref + https://builttoadapt.io/intro-to-the-8-point-grid-system-d2573cde8632 + https://builttoadapt.io/8-point-grid-borders-and-layouts-e91eb97f5091 + https://blog.prototypr.io/a-framework-for-creating-a-predictable-and-harmonious-spacing-system-8eee8aaf773c + https://spec.fm/specifics/8-pt-grid + **** font **** color, shade, shaddow ***** refactoring ui ***** lyft color - design.lyft.com/re-approaching-color-9e604ba22c88 *** design system **** protocol.mozilla.org **** material.io **** primer.style **** brand.uber.com **** evergreen.segment.com **** design.workday.com **** thumbprint.design **** ant.design **** www.designsystems.com **** orbit.kiwi *** SVG tools **** Inkscape - For logo design, icon design, illustation design - Free *** UI/UX tools **** Sketch - $99 one time - Only on Mac **** Adobe XD - Free with limited features - On Mac and Windows only **** Figma ***** founding - For web UI and UX - Free with limited features - Web app - Can use instead of Inkscape for simple logos, icons, illustrations + Better as no need to import/reimport after edit/update the logo/icon - Make logos/icons components so that when change, apply to all linked. And can apply directly to linked item for specific change - Components can not published with free plan, need to pay - Styles and Components are published to team level, not project level - As no shared components, need to do everything in 1 file! logo, illustrations, pages - Do everything in 1 file, performance issue? - In a file, can have several pages ***** workflow - team -> project -> file -> page - 1 file, 4 pages: design system, logo, illustrations, pages - Create local styles and local components in design system page + color pallete + 2 font types: font-head, font-content + size + weight + leading, line height + h1-h6 - Create frames in pages like + landing_desktop_pattern1 + landing_mobile_pattern1 + landing_android_pattern1 + landing_ios_pattern1 + main_desktop_ + main_mobile_ + main_android_ + main_ios_ *** Color tools **** colors.co **** colormind.io *** Contrast tools **** usecontrast.com **** webaim.org/resources/contrastchecker *** Font tools **** fontsinuse.com **** klim.co.nz **** hanken.co **** www.fontsquirrel.com **** typewolf.com *** Chrome extensions - responsive-viewer: chrome.google.com/webstore/detail/responsive-viewer/inmopeiepgfljkpkidclfgbgbmfcennb - PixelParallel: chrome.google.com/webstore/detail/pixelparallel-by-htmlburg/iffnoibnepbcloaaagchjonfplimpkob - VisBug: chrome.google.com/webstore/detail/visbug/cdockenadnadldjbbgcallicgledbeoc *** Free icons - github.com/refactoringui/heroicons, www.heroicons.com/ - www.zondicons.com - www.heropatterns.com - iconscout.com - icons8.com - fontawesome.com - useiconic.com - streamlineicons.com - github.com/tabler/tabler-icons - github.com/twbs/icons - systemuicons.com - iconbuddy.com *** Free illustrations - undraw.co - humaaans.com *** Free images - unsplash.com - www.istockphoto.com *** Inspiration - land-book.com - pages.xyz - www.awwwards.com - thefwa.com *** starter kit **** Predifined Tailwind CSS + Free templates **** pdkit.co **** antforfigma.com ($79) **** setproduct.com ($86) **** refactoring UI ($149) **** tailwindui.com ($249) ** css system *** bulma - Good for use as is *** tailwind - Customizable - Use with create_react_app + import './tailwind.css'; - ref + tailwindcss.com/ + tailwindcomponents.com + tailwindtoolbox.com/ + tailwindui.com + www.youtube.com/watch?v=J_7_mnFSLDg + github.com/aniftyco/awesome-tailwindcss *** misc. - debug.css - cssstats.com ** single web app for every page *** pages - landing/home page - list/delete/add/edit page - add via url page *** libraries **** yarn - yarn init **** babel - yarn add @babel/core @babel/cli @babel/preset-env @babel/preset-react --dev - create .babelrc at the project root **** webpack - yarn add webpack webpack-cli webpack-dev-server style-loader css-loader babel-loader --dev - create webpack.config.js at the project root **** react **** sass/scss + bulma **** ref - blog.usejournal.com/creating-a-react-app-from-scratch-f3c693b84658 *** starter-kits **** create-react-app ** gaia & sync *** founding - some sync requires implementation on both client and server i.e. synceddb (github.com/paldepind/synceddb), pouchdb (pouchdb.com), turtleDB (turtle-db.github.io) - manual sync is cumbersum + https://github.com/smebberson/array-sync - every time compare needs to load all items + keep timestamp at client and keep log files of changes at gaia, max log files might be 10, compare tiemstamps and apply changes, update changes to gaia (a log file) <- irony? + *easier with only add and delete?* use file names(MD5 of content + created dt + updated dt) for sync + load just file names to compare? order is important! + for bidirectional sync, how to know it's delete or add? + Too complicate, need CRDTs! - if there are *thousands items*, first time sync would take long time - IndexedDB and LocalStorage are temporary, not persistant - Most promising would be PouchDB + Gaia is wrapped by DownLevel and PouchDB connects to by leveldb -> PouchDB will treat it as local database but actually underneath blockstack API is called + Create idb as local database, update to this one and sync to the above one + No as required CouchDB! (pouchdb.com/faq.html) for Multiversion concurrency control(MCC) and IndexedDB has a limit! - Conflict-free Replicated Data Types (CRDTs) + might be too slow to download all change log and build final results + the list will keep growing with changes + ref: github.com/orbitdb/crdts, github.com/conclave-team/conclave *** ref - medium.com/@cody_84149/gaia-storage-with-leveldb-37112007e335 - hackernoon.com/building-conclave-a-decentralized-real-time-collaborative-text-editor-a6ab438fe79f - hackernoon.com/turtledb-a-javascript-framework-for-building-offline-first-collaborative-web-apps-7183cd787163 ** gaia & service worker & indexedDB & Cache API *** cache in indexedDB for faster startup - Check if there are items in indexedDB, if yes, show these first - In the background (might not need service worker), connect to Gaia and load latest items - if there is change, update UI with latest items - Save ONLY latest items in indexedDB for fast startup only! - Cache API might not suitable as the same request would return different response, except static files - *no need* to do this manually because there is *stale-while-revalidate* + www.toptal.com/react-hooks/stale-while-revalidate *** SWR for faster startup - github.com/zeit/swr - features + stale-while-revalidate + Focus Revalidation + Refetch on Interval + Scroll Position Recovery and Pagination - check + can work with gaia? yes + pagination? + add/edit/delete apply to cache? force update? force refetch? - alternative + react-query *** background job for add/edit/delete and save results in indexedDB for error handling - Save data in indexedDB first - Connect to Gaia to update - if succeed, clear indexedDB, if not retry - Need to be a queue - check + If add and then edit/delete while still connecting to Gaia? + If first error, how to handle next in the queue? - should use library i.e. redux-offline *** redux-offline for faster startup - *** redux-offline for add/edit/delete - github.com/redux-offline/redux-offline - can be used for faster startup too, no need SWR - *** require appropriate UI - loading - message *** caveat **** inconsistency in indexedDB / cache management - when first visit - when revisit - when refresh - when add/edit/delete items - when load more - when scroll down/scroll up *** tools **** workbox **** idb - www.npmjs.com/package/idb **** dexie.org - Wrapper for IndexedDB *** ref - developers.google.com/web - serviceworke.rs ** for each gaia server, one file per one link. performance issue? *** questions - each file, json data: url, title, image, web_favicon, just_web_name, beautifed_flag, uuid/guid, created_dt, updated_dt - forum.blockstack.org/t/proposal-create-a-document-collection-for-cross-app-data-sharing/9286/2 - github.com/blockstack/blockstack-collections - settings file - all notes in a folder named notes, settings.json is in root or another folder - should have version.json file? - how to set filename? worst case: same account creat a file at the same time on web and mobile? - read file one by one, performance issue? one round trip, get everything? *** gaia api - put file - list file names with pagination - read file ** How to extract web page content? - Use Google cloud storage to serve html/js/css files - If require, client (ReactJS) connects to server (Google App Engine) i.e. extract information from an url, security issue? + jsonp + use backend code + window.postMessage + Cross-Origin Resource Sharing (CORS) + ref: https://javascript.info/fetch-crossorigin, http://www.eriwen.com/javascript/how-to-cors/, https://www.moxio.com/blog/12/how-to-make-a-cross-domain-request-in-javascript-using-cors, https://jvaneyck.wordpress.com/2014/01/07/cross-domain-requests-in-javascript/, https://www.moesif.com/blog/technical/cors/Authoritative-Guide-to-CORS-Cross-Origin-Resource-Sharing-for-REST-APIs/ - Might need to be Python server, not NodeJS server as features needed are about information extraction i.e. beautifulsoup or libraries in NodeJS should be available - Believe that with Google App Engine standard enviroment, external libraries like beautifulsoup can be installed. ** website analytics *** tools - usefathom.com/, $14 per month -> too expensive? - Google analytics is free but privacy? - github.com/PostHog/posthog - self host - matomo.org - plausible.io - mixpanel.com ** monorepo - webapp, firefox extension, chrome extension, android with react native, ios with react native ** CI/CD *** Github actions - ** test This and CI/CD are very important! *** tools - github.com/enzymejs/enzyme - browserstack.com - lambdatest.com - pact.io - browsersync.io - developers.google.com/web/tools/lighthouse - cypress.io - a complete end-to-end testing experience - jest ** tools - jdenticon.com - generate a random image placeholder for a missing image - greensock.com - animation, not totally free - relay.dev - the production-ready GraphQL client for React - immortalDB - redundant store data in indexedDB, localStorage, and cookies - reqres.in - test your front-end against a real API - puppeteer - provides a high-level API to control headless Chrome ** resources - github.com/pujolchr/aStackToRead - github.com/ShafiqShams/reacttodosapp - github.com/bingex/react-crud - github.com/khanshamim/react-application - github.com/donovantc/react-shared - github.com/ksholla20/react-native-react-monorepo-starter ** react native *** blockstack-native **** requirements - sign up, sign in, sign out - listFiles, getFile, putFile, deleteFile *** tailwindcss for react-native **** requirements - width query: default, sm, md, lg, - custom classnames *** auto responsive grid layout **** requirements - 1 col, 2 cols, 3 cols - animation on add, remove - render on the fly like flatlist *** share extension **** requirements - share url to and save in a background *** change list names *** popup with back press *** animation * requirement ** Add link ** List views, view more ** Click to visit, Copy link, share link, delete link ** search/filter with regex pattern ** Getting started screen guide ** Extensions on firefox, chrome ** Mobile apps - react native ** Beautify - extract web page title, images ** Trash - 30 days before delete ** tagging/labelling/archieved * design user experience (UI/UX) ** theme - minimalism + youtu.be/fzwOBOjSto8?t=224 + redstapler.co/web-design-trends-2019/ + youtube.com/watch?v=z0GChBEw4BU + kaycinho.com/website-trends-2019-web-design/ ** inspiration - miro.com/stickies-capture - bizy.com - pros.com - upperquad.com - github.com/cssninjaStudio/fresh - webartisan.be - github.com (dark theme) - www.mozilla.org (white navbar) - bvd.se - brander.ua - adoric.com - www.schwarz-matt.com - minimalissimo.com - apple.com - wsj.com - medium.com - brand.uber.com - www.designsystems.com/sections/getting-started ** logo *** color - black/white/silver/grey: balance, neutral, calm i.e. Apple, Nike, Puma, Honda, Wiki, CartoonNetwork, Benz, wsj, gucci, channel, prada, nytimes.com, sony, uber, medium.com *** fonts for brandname - Moiser techno Font - www.fontspace.com/moiser-techno-font-f16409 - Chainsaw Geometric Font - www.fontspace.com/chainsaw-geometric-font-f9174 - gtek-minimal - www.qbotype.com/pagina-del-producto/gtek-minimal-1 - amirox - www.qbotype.com/pagina-del-producto/amirox-v16-1 - HFF Modern Strand Font - www.fontspace.com/hff-modern-strand-font-f9019 - misirlou - www.fontsc.com/font/misirlou ** icon - ** illustration - undraw.co/search -> collecting - undraw.co/search -> bookmarks ** color pallete - All black on logo and landing page, colorfy with moving shapes like upperquad.com - On landing page requires black-white illustrator - On main page, primary color is blue like pros.com and dark shade is like Blockstack color, others generate from colormind.io - tailwind css - lyft colors - brand.uber.com/guide#color-specialty-colors - Action color: dark grey (close to black) ** font - type + Helvetica Now Display, Helvetica Now Text + Rubik + Work Sans + tinkov.info/gilroy.html ($180) + Eina03 + MaisonNeue + Uber Move Text + choose from www.typewolf.com/google-fonts - size: 16px - line height: ? - color: ? - weight: ? - ref: https://ant.design/docs/spec/font ** layout *** attributes - display-type/position-type - margin/padding - width/height - color - font-size - font-color - font-weight - line-height - letter-spacing - border-size - border-color - corner-radius ** landing page *** nav - BRACE *** 1st section - Save links to visit later - Blockstack login/signup button - *illustrator* *** 2nd section - Easy ways to save - 1. click add bottom - 2. type brace.to/ at url bar *** 3rd section - All in one place - *Show main page with beautiful contents* *** 4th section - Privacy, your own identity and full control of you data *** 5th section - Start saving now! - Blockstack login/signup button *** footer - twitter, github - about, terms of service, privacy - faq + How different from pocket? + What are the benefits of using Brace? + ** main page *** Auto Responsive Grid Layout - github.com/xudafeng/autoresponsive-react - github.com/tsuyoshiwada/react-stack-grid - github.com/dantrain/react-stonecutter ** main page w/o items *** tutorial - * Implementation ** project *** create-react-app - medium.com/@xijo/create-react-app-with-tailwind-via-postcss-plus-purgecss-5c36b4c33ba7 *** dependencies - tailwindcss - postcss-cli - autoprefixer - purgecss - react - react-dom - react-scripts + webpack + babel + postcss + autoprefixer + workbox - redux - react-redux - axios - redux-thunk - (No use redux-form for more control over action and reducer) - redux-offline - (No use router for more customization) - (No use context as use redux) - ? hook ? - blockstack-js - blockstack-connect - url-parse ** React *** entry point - index.js *** components - App.js - Loading.js - Landing.js - TopBar.js + redux-form on search and add - BottomBar.js - CardList.js - CardItem.js - Settings.js - Adding.js - DialogBox.js (Modal.js) - Popup.js + If use redux, there will be only one popup, but how to click outside to close? - Link.js *** router - switch (medium.com/@daveford/react-router-alternative-switch-acd7961f08db) - if switch is not ok, use router5 (life.wongnai.com/router5-39d97bd9e48d) ** Redux & Redux-Offline *** state - link + version + id (UUID) + url (+ domain_name) + No need -> list_name: default, archive + added_dt + did_beautify + title + favicon + screenshot + beautified_dt + No need -> did_remove + No need -> removed_dt - links: {key is link id, value is an object of its values}, sort? + MyList ~ adding ~ added ~ removing + Trash ~ adding ~ added ~ removing + Archieve ~ adding ~ added ~ removing *** actions - SIGN_IN - SIGN_OUT *** reducers - userReducer ** Workbox ** Blockstack (js, Gaia, connect) *** sign in *** sign out *** fetch with pagination. sort? - featch all file with listFiles - save all files in root path both links and settings so that when calling listFiles, all files come at once *** save. id? ** Auto Responsive Grid Layout - ** Search - lucaongaro.eu/blog/2019/01/30/minisearch-client-side-fulltext-search-engine.html ** API server *** Google app engine *** Puppeteer ** steps - yarn create react-app bracedotto --template cra-template-pwa - yarn add tailwindcss postcss autoprefixer - yarn add @tailwindcss/forms @tailwindcss/typography @tailwindcss/aspect-ratio @tailwindcss/line-clamp - no need for dev dependencies as this's not a node app - yarn add redux redux-loop react-redux reselect - yarn add https://github.com/stxapps/redux-offline#028205cec27dcf1ae05e310fc7e45779ee908949 - yarn add @stacks/auth @stacks/storage @stacks/wallet-sdk @stacks/connect @stacks/encryption - Make sure history works: + Address bar i.e. change path, change hash! + redirect back i.e. from blockstack or other websites + back, forward button + button, tag in the app - yarn add url-parse - Hack for back button + Need 2 history states for when press back, the previous state still be our app and can handle in onpopstate + Need to provide each history state a number for knowing back press or forward press or tag click + ref: stackoverflow.com/questions/8980255/how-do-i-retrieve-if-the-popstate-event-comes-from-back-or-forward-actions-with/49329267#49329267, medium.com/@subwaymatch/disabling-back-button-in-react-with-react-router-v5-34bb316c99d7 - yarn add react-graceful-image - yarn add axios - yarn add file-saver - yarn add @welldone-software/why-did-you-render - yarn add framer-motion - yarn add @types/node @types/react @types/react-dom @types/jest @types/url-parse @types/file-saver - yarn add @wewatch/lexorank - webpack 5 no longer include polyfills for node.js core modules by default + yarn add react-app-rewired + call react-app-rewired in package's scripts + create a config-overrides.js file in the root directory + install all polyfills: yarn add assert buffer crypto-browserify process stream-browserify util - yarn add blueimp-load-image - yarn add idb-keyval - yarn add jest-diff ** attribution - tailwindcss, postcss, purgecss - uirefactoring - figma - iconmonstr.com - undraw.co - getpocket.com - saved.io - udemy - react, create-react-app - redux, redux-thunk, redux-offline, react-redux - github.com/ConnorAtherton/loaders.css - loading.io/css/ - react-stack-grid - react-graceful-image - jdenticon - leaverou.github.io/css3patterns - freefrontend.com/css-background-patterns - images + silver-watch-newspaper-magazine-preview.jpg: https://www.pickpik.com/silver-watch-newspaper-magazine-flower-pink-69937 + black-and-white-electronic-device-cup-of-espresso-on-saucer-beside-spiral-note.jpg: https://www.pikrepo.com/frbdj/black-and-white-electronic-device-cup-of-espresso-on-saucer-beside-spiral-note + silver-macbook-beside-white-smartphone.jpg: https://www.pikrepo.com/fivtc/silver-macbook-beside-white-smartphone + green-typewriter-beside-green-hardbound-book-and-book.jpg: https://www.pikrepo.com/fenlp/green-typewriter-beside-green-hardbound-book-and-book + cup-of-coffee-beside-gray-laptop-on-brown-wooden-desk.jpg: https://www.pikrepo.com/femfv/cup-of-coffee-beside-gray-laptop-on-brown-wooden-desk + macbook-air-beside-white-ceramic-mug-on-brown-wooden-table.jpg: https://www.pikrepo.com/fevvb/macbook-air-beside-white-ceramic-mug-on-brown-wooden-table + person-using-gray-laptop-computer.jpg: https://www.pikrepo.com/fefte/person-using-gray-laptop-computer + black-and-gray-laptop-computer-macbook-beside-drinking-glass-on-wooden-table.jpg: https://www.pikrepo.com/fnejx/black-and-gray-laptop-computer-macbook-beside-drinking-glass-on-wooden-table + black-ceramic-mug-near-three-smartphones-on-white-wooden-desk.jpg: https://www.pikrepo.com/fprob/black-ceramic-mug-near-three-smartphones-on-white-wooden-desk + gold-iphone-6-on-white-book-near-gray-laptop-computer.jpg: https://www.pikrepo.com/fefsm/gold-iphone-6-on-white-book-near-gray-laptop-computer + cup-of-coffee-beside-macbook-pro.jpg: https://www.pikrepo.com/fpgac/cup-of-coffee-beside-macbook-pro + silver-iphone-6.jpg: https://www.pikrepo.com/felsy/silver-iphone-6 + silver-iphone-6-beside-white-ruled-paper.jpg: https://www.pikrepo.com/fntiz/silver-iphone-6-beside-white-ruled-paper + eyeglasses-with-black-plastic-frame-on-top-of-open-book.jpg: https://www.pikrepo.com/flqlq/eyeglasses-with-black-plastic-frame-on-top-of-open-book + macbook-pro-on-brown-table.jpg: https://www.pikrepo.com/fpgml/macbook-pro-on-brown-table + white-ceramic-teacup-with-saucer.jpg: https://www.pikrepo.com/ferbh/white-ceramic-teacup-with-saucer + white-pen-on-white-notebook-beside-the-orange-ceramic-coffee-cup-with-saucer-located-on-top-of-white-table.jpg: https://www.pikrepo.com/fqdhp/white-pen-on-white-notebook-beside-the-orange-ceramic-coffee-cup-with-saucer-located-on-top-of-white-table + white-ipad-on-brown-wooden-table.jpg: https://www.pikrepo.com/fffbs/white-ipad-on-brown-wooden-table + macbook-and-teal-ceramic-kettle-on-white-table.jpg: https://www.pikrepo.com/frwgk/macbook-and-teal-ceramic-kettle-on-white-table + gray-zenit-e-slr-camera-on-brown-surface.jpg: https://www.pikrepo.com/femzx/gray-zenit-e-slr-camera-on-brown-surface + eyeglasses-on-top-of-opened-book.jpg: https://www.pikrepo.com/flobr/eyeglasses-on-top-of-opened-book + turned-off-black-laptop-computer-on-green-grass-field.jpg: https://www.pikrepo.com/ferad/turned-off-black-laptop-computer-on-green-grass-field + pencil-with-gray-sharpener-on-notepad.jpg: https://www.pikrepo.com/feayq/pencil-with-gray-sharpener-on-notepad + clear-light-bulb.jpg: https://www.pikrepo.com/frelq/clear-light-bulb + brown-wooden-framed-chalkboard.jpg: https://www.pikrepo.com/fllvu/brown-wooden-framed-chalkboard + clear-light-bulb-on-black-chalkboard.jpg: https://www.pikrepo.com/flsjo/clear-light-bulb-on-black-chalkboard + clear-sodium-bulb-lot-with-white-background.jpg: https://www.pikrepo.com/fllvk/clear-sodium-bulb-lot-with-white-background + happiness-illustration.jpg: https://www.pikrepo.com/fqlyh/happiness-illustration + led-bulb-on-black-surface.jpg: https://www.pikrepo.com/fodsy/led-bulb-on-black-surface + clear-halogen-bulb.jpg: https://www.pikrepo.com/frucq/clear-halogen-bulb + macbook-pro-full-of-sticky-notes.jpg: https://www.pikrepo.com/fpgbt/macbook-pro-full-of-sticky-notes + white-notebook-with-gray-ballpoint-pen.jpg: https://www.pikrepo.com/fpyvi/white-notebook-with-gray-ballpoint-pen + blue-ballpoint-pen-on-white-ruled-notebook.jpg: https://www.pikrepo.com/fentc/blue-ballpoint-pen-on-white-ruled-notebook + white-printing-paper-on-table.jpg: https://www.pikrepo.com/frmeg/white-printing-paper-on-table + white-notepad-between-color-pens.jpg: https://www.pikrepo.com/frcst/white-notepad-between-color-pens + black-ballpoint-pen-on-graphing-notebook.jpg: https://www.pikrepo.com/fobcp/black-ballpoint-pen-on-graphing-notebook + silver-framed-eyeglasses-beside-white-click-pen-and-white-notebook.jpg: https://www.pikrepo.com/frrsu/silver-framed-eyeglasses-beside-white-click-pen-and-white-notebook * mvp ** No extensions yet - Put app name on url - Go to webapp and add manually ** blockstack apis - register - sign in - sign out - retrieve all bookmarks - create a bookmark - delete a bookmark ** monorepo ** react - state, route, view * fully-fledged ** todo - BottomBar: jumpy when add popup transition with keyboard - On mobile, just use div, not GridLayout + can use Tailwindcss space-y + no animation! - Plus sign at the top of the page, what's about mobile? ** features - download all - delete all - delete account - create a new list - set default list - bulk edit - empty trash - select pattern or extract content - sort, rearrange? ** webapp, firefox/chrome extensions, android, ios * servers ** tailwindcss's live-server - http://localhost:8080 - http://192.168.43.220:8080 ** create-react-app's webpack dev server - https://localhost:3000 - https://192.168.43.220:3000 ** test server - https://d2r7uroqj51uls.cloudfront.net ** prod - https://brace.to * devices - Android phone: Firefox, Chrome - iphone (emulator): Firefox, Chrome, Safari - iPad: Firefox, Chrome, Safari - Macbook: Firefox, Chrome, Safari - Ubuntu laptop: Firefox, Chrome * react-native ** steps - npx @react-native-community/cli@latest init Bracedotto --pm npm - npm i redux@4.2.1 react-redux@8.1.3 redux-loop@6.2.0 reselect@4.1.8 - npm i react-native-svg - npm i -D react-native-svg-transformer + Update metro.config.js to work with .svg - npm i url-parse - npm i react-native-animated-spinkit react-native-swiper - npm i react-native-safe-area-context - npm i react-native-keyboard-manager - npm i react-native-webview - npm i react-native-file-access - npm i https://github.com/stxapps/tailwind-rn#96848a8d2c2370e4f5e57a773ace172c4073157d - npm i 'https://gitpkg.now.sh/stxapps/blockstack-react-native/BlockstackSDK?3d5bcd6ea9a17f00c76be5913009f2c02f13b95f' - npm i https://github.com/stxapps/redux-offline#028205cec27dcf1ae05e310fc7e45779ee908949 - npm i @react-native-async-storage/async-storage @react-native-community/netinfo - npm i @welldone-software/why-did-you-render -D - npm i react-native-iap @wewatch/lexorank axios - npm i https://github.com/stxapps/react-native-device-time-format#63f4a9158a91c391d67317a489ccfe8ec28a7c84 - npm i react-native-document-picker react-native-share @react-native-clipboard/clipboard - npm i react-native-flag-secure - npm i jest-diff - npm i react-native-default-preference - npm i https://github.com/stxapps/react-native-receive-sharing-intent#d1b4aa9140e0ad28e1a8403814125a733c92172c - npm i react-native-system-navigation-bar @vonovak/react-native-theme-control - npm uninstall typescript && npm i typescript@5.5.4 -D - npm i react-native-image-crop-picker - npm i react-native-modal react-native-popup-menu - npm i @d11/react-native-fast-image - change minSdkVersion to 28 in ./android/build.gradle - Allow CORS access on https://brace.to/manifest.json - Set up Android deep linking: reactnative.dev/docs/linking and developer.android.com/training/app-links/deep-linking - npx react-native-asset to link assets i.e. fonts + medium.com/@mehrankhandev/ultimate-guide-to-use-custom-fonts-in-react-native-77fcdf859cf4 - ios + Edit PodFile: ios target 15.5, pod 'Blockstack' + First time must run: ~ cd ios ~ bundle install ~ bundle exec pod install + In Xcode, Bracedotto General -> Target iOS 15.5 + In AppDelegate.m, moduleName:@"Brace" + Select Bracedotto in left pane, new -> swift file and generate header binding + Add image assets, update storyboard + Targets -> Bracedotto -> Build Settings -> Build Options -> Always embed Swift standard libraries: $(inherited) - ios share extension + Target Bracedotto -> Info -> URL Types -> URL Schemes: bracedotto + Add RCTLinkingManager in AppDelegate.m + File -> New -> Target -> Share Extension ~ Save to Brace, Swift + Target Save to Brace -> General -> target iOS: 12.0 + In Info.plist under Save To Brace ~ NSExtension -> NSExtensionAttributes -> NSExtensionActivationRule -> NSExtensionActivationSupportsWebURLWithMaxCount -> Number 1 + Update ShareViewController.swift - Add App groups from targets: Bracedotto and Save to Brace -> Signing & Capabilities + group.bracedotto.share - CryptoSwift and NVActivityIndicatorView dependency + Add the dependency in Podfile with target 'Save to Brace' - Add fonts to Save to Brace + Xcode's sidebar -> Resources, select all fonts + Xcode's rightbar -> Target Membership, select Save to Brace + Save to Brace -> Info.plist, add Fonts provided by application - Targets -> Bracedotto -> Build Settings -> Packaging -> Product Name: Brace * Debug ** Flipper *** iOS - stackoverflow.com/a/74030603 - show hidden files: press the “Command” + “Shift” + “.” (period) keys * test cases - web, mobile - PC, laptop, tablet, phone - features + Landing, About, Terms, Privacy, Support + Sign up, Sign in + Main + List links, fetch more + New link ~ bottom bar, top bar, empty page, share extension + move link, delete link ~ bottom bar, top bar + Bulk edit: move notes, permanently delete notes + Search ~ add new link, move/delete link, bulk edit ~ pin, unpin, move pin + List list names, change list name + Card, List + New list name, edit list name, move list name, delete list name + Popups: CardMenu, ConfirmDelete, MoveTo, ListsMenu + Settings: account, subscription, data, lists, misc, about + Pin, unpin, move pin + Custom link's title & image + WHT_MODE, BLK_MODE * brace-server-worker ** Cloud Run and no need all below - yarn run deploy - might need to increase memory - Set schedule */30 * * * * ** Creat an instance - us-central1-a - Choose e2-medium (2 vCPU, 4GB memory) - Ubuntu 20.04LTS on balanced persistent disk 10 GB - Allow full access to all Cloud APIs - **Uncheck** Delete boot disk when instance is deleted ** Access the instance - gcloud compute ssh --zone=us-west1-b brace-worker-002 - If found WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! + exit and run the command explained below the warning i.e. ssh-keygen -f "/home/linux/.ssh/google_compute_known_hosts" -R "compute.8353753600559446613" ** Setup the instance - sudo apt update, sudo apt upgrade - install nvm, nodejs, yarn - git clone https://github.com/stxapps/brace-server-worker.git - cd brace-server-worker - git pull - yarn - if puppeteer errors, try stackoverflow.com/questions/66214552/tmp-chromium-error-while-loading-shared-libraries-libnss3-so-cannot-open-sha + ldd node_modules/puppeteer/.local-chromium/linux-XXX/chrome-linux/chrome | grep not - curl -sSO https://dl.google.com/cloudagents/add-google-cloud-ops-agent-repo.sh && sudo bash add-google-cloud-ops-agent-repo.sh --also-install + cloud.google.com/stackdriver/docs/solutions/agents/ops-agent/installation ** Add a startup script - As startup script is executed by root user, need to specify running commands by linux user - use 'sudo -i' to switch to root user and test the script below - startup script in Custom metadata, key: startup-script, value: #! /bin/bash sudo -u linux bash -c 'cd /home/linux/brace-server-worker; mkdir -p ./logs; NOW=$(date +'%Y%m%d%H%M%S'); /home/linux/.nvm/versions/node/v16.18.1/bin/node --experimental-specifier-resolution=node src/index.js > "./logs/log_${NOW}.txt" 2>&1; sudo shutdown -h now' - *Check node version as it's hard coded!* ** Schedule the instance - IAM + Check include Google-provided role grants + Edit on service-57708091218@compute-system.iam.gserviceaccount.com + Add role Compute Instance Admin (v1) - Under VM instances, select tab Instance Schedule + Name: brace-server-worker-scheduler + Region: us-central1 + Start CRON expression: 0 * * * * + Stop CRON expression: 20 * * * * (at least 15 mins apart) + Time zone: Indochina Time (ICT) + Add the instance to the schedule - ref: cloud.google.com/compute/docs/instances/schedule-instance-start-stop ** Fix disk full - df -k - likely it's snaps: du -h /var/lib/snapd/snaps - remove old snap: sudo bash remove-old-snaps.sh - ref: itsfoss.com/free-up-space-ubuntu-linux/ * Clean up GCloud DataStore - Use GCloud console - Delete error extracted results + select * from ExtractedResult where status = 'EXTRACT_ERROR' - Delete old extracted results + select * from ExtractedResult ORDER BY extractedDT * In-App Purchase ** Setup in Connect - Need to provide a screenshot and description for review - Get App-Specific Shared Secret in Features: In-App Purchases tab - Sandbox Testers + Use an email address that has never been used as an Apple ID or used to purchase iTunes or App Store content. Consider creating a dedicated email address for each sandbox tester. - Add server url to URL for App Store Server Notifications in General -> App information -> v2 - Add a link Terms of Service in the App Description ** Setup in XCode - Add In-App Purchase capability in Xcode - When testing in-app purchases in the sandbox environment, make sure you’re running a development-signed build of your app; production-signed builds use the production environment. ** Setup in React Native - yarn add react-native-iap (StoreKit original API) - Add missingDimensionStrategy 'store', 'play' in defaultConfig in android/app/build.gradle ** Setup in Play console - Release to Closed testing: Alpha - Add Testers and the license testers + Join in https://play.google.com/apps/testing/com.bracedotto - Add iap subscription and activate - Use same versionCode and versionName as in Closed testing - Setup -> API access (account level, both apps can use it) + Create a new Google Cloud Project + Create new service account, choose Editor for the project access. Allow all permissions for financial data, create new key ** Setup in Google Cloud - Add pub/sub topic: projects/iap-001/topics/playstore-notifications-bracedotto + Justnote can also use this pub/sub topic and the subscriber! + Should name just playstore-notifications as can reuse with others! - Change to push with the server endpoint, Set the subscriber to never expire - Add permission to the topic - Add the topic in Play Console -> Brace.to app -> Monetization setup ** Setup in iap-server - cd Drive/Workspace/iap-server - gcloud app create --project=iap-001 --region=us-central - assume unchanged: git update-index --assume-unchanged src/appstore-keys.json src/playstore-service-account.json src/paddle-keys.json - yarn add googleapis node-apple-receipt-verify - Create Application Default Credential (ADC) + select project: iap-001, go to IAM & Admin -> Service Accounts -> Select App Engine default service account -> Keys -> Add Key -> Create new Key + Move the file to ~/.config/gcloud/legacy_credentials/brace@brace.to + Refer to it in package.json -> scripts -> start-local + cloud.google.com/nodejs/docs/reference/datastore/latest/datastore/datastore + cloud.google.com/docs/authentication/production#providing_credentials_to_your_application ** Test *** iOS **** XCode (developer.apple.com/documentation/xcode/setting-up-storekit-testing-in-xcode) - Create supporter.storekit - In Edit Scheme... -> Run -> Options, Storekit Configuration: supporter.storekit - Choose Build Configuration: Debug, Debug executable, and localhost in Info.plist - Choose supporter.storekit before + Test interrupted Purchases by Editor -> Enable Interrupted Purchases + Test failed transactions by Editor -> Fail transactions -> SKErrorUnknown + Manage Transactions by Debug > StoreKit > Manage Transactions - Don't forget to change Storekit Configuration back to None **** Sandbox (help.apple.com/app-store-connect/#/dev7e89e149d) - Don't need to sign out, just run the app in *debug mode*, when make a purchase, there will a popup to use test user - Sign in with test id: testuser01@brace.to, normal pass + Conf in the popup - Manage the test user in Settings -> App Store -> Sandbox account *** Android - Join a test program - Choose approve purchase, or reject purchase in both purchase popup and in subscription management in Google Play App - Choose cancel subscription in subscription management in Google Play App * Expo ** install - npx create-expo-app --template tabs - npx expo install expo-dev-client -- --save-dev - npm i file:../../../sdrive-hub-sdk/blockstack-react-native/react-native-blockstack-0.1.1.tgz + In the blockstack-react-native, run npm pack - npx expo install react-native-flag-secure - npm i react-redux redux redux-thunk reselect - npx expo install redux-devtools-expo-dev-plugin - npm i url-parse @wewatch/lexorank - npm i jest-diff@29.7.0 - npx expo install expo-share-intent - npx expo install react-native-file-access - npx expo install expo-localization - npx expo install expo-iap - npx expo install expo-document-picker - npx expo install react-native-svg - npx expo install react-native-svg-transformer -- --save-dev - npx expo install react-native-webview - npx expo install react-native-keyboard-controller - npx expo install react-native-animated-spinkit - npx expo install react-native-share - npx expo install expo-build-properties - npx expo lint - npx expo install react-native-navigation-mode - npx expo install @react-native-async-storage/async-storage - npx expo install @react-native-community/netinfo - npm i https://github.com/stxapps/redux-offline#028205cec27dcf1ae05e310fc7e45779ee908949 - npm i react-native-swiper - npx expo install expo-clipboard - npx expo install expo-image-picker expo-image-manipulator - npx expo install expo-image - npx expo install @stylistic/eslint-plugin -- --save-dev - npx expo install expo-system-ui - npx expo install react-native-edge-to-edge ** build - npx expo prebuild --clean - ios + open ios/Brace.xcworkspace + choose Brace on the right pane + targets: Brace, on General: ~ update Version, App Category: Productivity, Display Name: Brace + targets: Brace, on Signing & Capabilities, add In-App Purchase + targets: Brace, on Build Phases, add Target Dependency: SavetoBrace + targets: SavetoBrace, on General: ~ update Version, Miminum iOS, Display Name: Save to Brace + targets: SavetoBrace, on Signing & Capabilities, select team - android + copy brace-android-upload-key.keystore to android/app + update keystore info in android/gradle.properties + update signingConfigs/release in android/app/build.gradle + change in buildTypes/release signingConfig to signingConfigs.release * Next ** Install - npx create-next-app@latest bracedotto + Choose typescript, tailwind and eslint + scripts dev - add --experimental-https --hostname - npm i redux react-redux redux-thunk reselect - npm i https://github.com/stxapps/redux-offline#028205cec27dcf1ae05e310fc7e45779ee908949 - npm i -D @tailwindcss/forms @tailwindcss/typography - npm i @stacks/auth@6.16.1 @stacks/encryption@6.16.1 @stacks/storage@6.16.1 @stacks/wallet-sdk@6.16.1 - npm i url-parse react-graceful-image file-saver @wewatch/lexorank blueimp-load-image idb-keyval - npm i jest-diff@29.7.0 - npm i motion - npm i p-queue - npm i @serwist/next && npm i -D serwist - npm install -D @types/url-parse @types/file-saver - npm install -D @stylistic/eslint-plugin - npm i eslint-plugin-tailwindcss@beta -D ** Steps on AWS 1.1 In AWS S3 -> Permissions -> CORS [ { "AllowedHeaders": [ "*" ], "AllowedMethods": [ "GET", "HEAD" ], "AllowedOrigins": [ "*" ], "ExposeHeaders": [], "MaxAgeSeconds": 31536000 } ] 1.2 Create custom cache policy: CustomUseOriginCacheControlHeaders-QueryStrings 1.3 Create custom origin request policy: CustomCORS-S3Origin 1.4 Create CF functions: test-brace-web-rewrite, brace-web-rewrite 2.1 upload files to S3, don't invalidate CF yet! 2.2 In AWS S3 -> Properties -> Static Website Hosting -> Error document change to 404.html 2.3 Set CF behavior -> Cache key and origin requests to recommended Cache: CustomUseOriginCacheControlHeaders-QueryStrings Origin request: Managed-CORS-S3Origin 2.4 Set CF behavior -> Function associations -> Viewer request -> brace-web-rewrite 2.5 invalidate CF