Git Product home page Git Product logo

tottem's Introduction

Tottem is an open source experimentation, it aims combining personal productivity tool approach with (slow) social media capabilities to make users empowered and somehow emancipated.

Tottem

Library management made social

I have two considerations in mind:

  • building a product based on ethic design
  • experimenting open-source web technologies and share it

Summary

Tottem

Tech

First goal: experimenting the tools that are available to build web software in 2020. This documentation explains which technologies are used here and how they are organised.

Codebase

Main technologies

It's a full-stack Typescript app with some code generation in order to have a type safe experience from end-to-end.

Here is a list of main technologies used:

Repository structure โ€” front-end

Inspired by those recommendations, this is how the codebase is structured:

tottem/
โ”œโ”€โ”€ api # contains graphlq endpoint based on Apollo Server & Prisma2
โ”œโ”€โ”€โ”€โ”€ prisma # contains model definitions & database migration logbook
โ”œโ”€โ”€โ”€โ”€ src/schema # contains graphql resolvers using Prisma Client
โ”œโ”€โ”€ src
โ”œโ”€โ”€โ”€โ”€ generated # contains generated code (types, hooks, ...)
โ”œโ”€โ”€โ”€โ”€ pages # static and dynamic routes declaration used by NextJS
โ”œโ”€โ”€โ”€โ”€ components # shared generic component
โ”œโ”€โ”€โ”€โ”€ scenes # different parts of the application
โ”œโ”€โ”€โ”€โ”€โ”€โ”€ moduleName # Auth | Profile | Me ...
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ components # module components. **Each** components can specify its own specific components, queries, ...
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ queries.gql # All data queries and mutations are written in gql files
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ hooks.ts # Most of the reusable logic is written in hooks
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ index.tsx # Main scene file
โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ View.tsx # Sometime stateless component are isolated in View file for clarity or reusability
โ””โ”€โ”€โ”€โ”€ services # shared services as authentication, error management, ...

How is it typesafe from end-to-end?

  • Prisma provides a library Photon that generate a typesafe client to manipulate data depending on a unique schema declaration in schema.prisma file.

Example where Photon is used to retrieve the not softly deleted items from specific collection :

const items = (
    await ctx.photon.items.findMany({
        where: {
            collection: { id: collectionId },
            isDeleted: false,
        },
        select: { id: true, position: true },
        orderBy: { createdAt: 'desc' },
    })
  • Nexus provides a code-first graphql approach that allows you to generate graphql schema (schema.graphql file) based on your resolvers and object definitions. Nexus is fully compliant with prisma and offers a nice plugin to automatically declare resolvers based on your photon client.

  • graphql-codegen tool is configured to parse all .gql front-end files containing graphl queries and mutations. graphql-codegen uses remote and local (localSchema.gql) graphql schemas to generate every type and hook we need (putting them inside types.ts) so we can safely fetch and mutate data.

Note that when global state management is required, Apollo Client is used as much as possible.

Example where typesafe hook useGetItemsQuery is generated allowing to fetch data via Apollo Client smoothly

const { data } = useGetItemsQuery({
        variables: {
            collectionId,
        },
    })

๐Ÿคฏ No typo anymore, much less files & context switching with typescript โœจ

Global state management

Most of the time, global state approach is used to avoid props drilling. Neither Redux or React Context API are used here. It has been implemented with Apollo Client. A local schema is defined in localSchema.gql

type Breadcrumb {
    title: String!
    href: String!
}

extend type Query {
    breadcrumbs: [Breadcrumb!]!
}

Then, we can define

  1. Query to read data from Apollo cache
query getBreadcrumbs {
    breadcrumbs @client {
        title
        href
    }
}

codegraphql-code is configured to generate this simple hook query, used in Profile/TopBar for instance:

const { data } = useGetBreadcrumbsQuery()

2 Custom hooks to write data to cache with Apollo client

const useBreadcrumbs = (profileSlug: string) => {
    const client = useApolloClient()

    const setBreadcrumbs = ({ breadcrumbs }: GetCollectionProfileQuery) => {
        client.writeData({
            data: {
                breadcrumbs,
            },
        })
    }

    const resetBreadcrumbs = () => {
        client.writeData({
            data: {
                breadcrumbs: [],
            },
        })
    }

    return { resetBreadcrumbs, setBreadcrumbs }
}

SSR Workflow

NextJS provides SSR features that make user experience awesome. NextJS comes with the concept of pre-rendering built-in, that can take 2 forms:

  • Static Generation
  • Server-side rendering

When developing an app, pages are usually not static and need to be rendered on-demand depending on the context (ie. user). NextJS documentation is great. However, it can be a bit confusing and hard to deeply understand what's happening when we use NextJS / SSR. This is a sequence diagram aims explaining how NextJS SSR works in Tottem case:

Tottem

Deployment

The app is fully deployed on Zeit Now. Configuration can be found in now.json file. Merging on master trigger new deployment.

The API runtime is serverless and run via Now Serverless Functions. As described here, API performance with Prisma Client is pretty good even with coldstart.

Setup

Locally PG instance is needed with some var env set. In .env for instance

AUTH0_LOCAL_ID='auth0|5dc8800986c8ba0e74d73654' # set local user id by passing auth0
AUTH0_CALLBACK='http://localhost:3000/auth/callback'
DATABASE_URL="postgresql://XXX@localhost:5432/XXX?sslaccept=accept_invalid_certs"
GRAPHQL_URL='http://localhost:4000/graphql'
DATABASE_PROVIDER="postgresql"

Then, two repositories are needed

git clone [email protected]:poulainv/tottem.git
cd tottem
npm install
npm run dev
cd tottem
cd api
npm install
npm run dev

Web app is available on http://localhost:3000 and graphql endpoint on http://localhost:4000/graphql

Product

Second goal: designing a product human centered allowing people to build and manage their online and public library. A tool to manage and gather the content we love, in order to share it with friends & community ๐Ÿ˜‡

Tottem

Why ?

More and more, people โ€” especially journalists, are losing their ability to choose which content to promote & amplify. Instead, automatic recommendation algorithms carefully choose the best (sic!) content to amplify. As reader, those deep learning algorithms create a unique & personal narrative stream of content in your social feed... How it can be the best ? Not really, it's just designed to maximize clicks and views. Of course, what else they can do?

Here, I'm happy to share some references

So, what if I want to explore durable book or article recommendations from a friend? What if I want to really dig into a specific subject?

How ?

Tottem aims combining personal productivity tool approach with (slow) social media capabilities to make users empowered and somehow emancipated.

Tottem aims to provide the same high quality user experience that most of modern productivity tools provide. Managing your library should be easy and enjoyable. With a great tool, great content could be made and shared.

The basic workflow:

  1. Collect everything in one Inbox.
  2. Organise into Spaces and Collection.
  3. Express yourself and explain your opinion
  4. Publish and share with your community

Tottem

Design

The UI/UX design is obviously inspired by Notion and Things 3. The design work made with Figma is available there

Tottem

Contributors

tottem's People

Contributors

deonclem avatar dependabot[bot] avatar dustywusty avatar dzigg avatar poulainv avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tottem's Issues

Migrate to Ant design

Main interested feature of Grommet are not used anymore (ResponsiveContext, Typography Theming), since usage of SSR with NextJs
As we will soon build more complex composant (Affix, Input, ...). We should use a more robust framework

Implement all cards component

Implement cards component of those components

  • Movies
  • Article
  • Influencers
  • Podcast

Then add sample data to display those fixtures in profile!

Having to click "more" on mobile to see the rest of the collection is not intuitive enough

[From an extenal user test]

When scrolling horizontally on a collection on mobile, it doesn't feel intuitive that some items are hidden (with only the first 4 displayed) and that you have to click "More" to see the rest of the items.

My proposition:

  • Either, on mobile, display a "+ X more" item at the end of the scrollable div, kind of like:

image
(mockup done in 30s don't be harsh๐Ÿ˜›)

  • Or simply let's not limit to the first 4 on mobile and allow the user to directly see the whole collection without having to click?

View more items button

Only 4 items are displayed by default

Screenshot 2019-09-17 at 18 11 28

A button View more should be displayed when more than 4 items are specified. Additional items are loaded on the same page after current elements and button Show less allow to hide them.

Screenshot 2019-09-17 at 18 13 13

Design still to be improved

The card hover is buggy and inefficient

I won't make a video but, especially on mobile the experience is not cool enough. I feel the card hover display is a bit delayed.
Moreover, there is a bug on mobile. To reproduce :

  • Go home page with mobile phone
  • Click on a card, see the hover card
  • Change tab
  • Click on a card from this new tab section, nothing happen
  • Click on another card, the hover card is displayed

Expected behaviour
Once section changed the hover card should be displayed when it's clicked on mobile!

I tried an implemention with CSS & JS but I am not sure about elegance & performance. Can you take a look @deonclem ?

Auth0 help?

Awesome project! Thanks for sharing ๐Ÿ™ .

I'm attempting to configure a local setup with my own Auth0 account, but have run into a few head scratchers.

Would you please describe how you configured your app and API in Auth0, including any special roles, permissions, etc? This might be something to add to the readme documentation, as well ๐Ÿคท ?

Also, I noticed a few places where 'tottem.app' URLs are hard coded.. Any reasons these don't reference env variables?

Thanks!
Ethan

Signing in to the platform should create a new user with empty collections

  • Signing in to the platform should prompt the user to pick a username.
  • A username should be unique
    The user will be pushed to the db, with empty collections for now.
    Bonus: replace the "sign in to auth0.com" inside the google login dialog
  • Ask for OAuth credentials on google account
  • Create the /username page on account creation

The username should be unique, the user will access its profile through tottem.app/username

It should probably be done outside of the Auth0 workflow - so we can check for username uniqueness.

Cards are not well aligned on mobile

When I resized the screen flexbox cards are wrapped but on my screen the second lines is not aligned. It's due to inserted box (because of gap parameters). I wonder if we should use Grid instead of flexbox to handle this behaviour. Wdyt?

Screenshot 2019-09-17 at 18 13 13

Tabs system

For some major profiles user can have a lot of data. Tabs allow items to be categorised into different section. I suggest to create new field section in items data in order to specify Tab names. Corner cases should be discussed. Tab system should be optional.

Replacing auth0 with next-auth

Could anyone guide me how to use the next-auth package (in this source code) for Authentication instead of using the third-party authentication which is super expensive.

How to separate logic when updating Apollo cache that used as global store?

Using Apollo cache as global store - for remote and local data, is very convenient.

However, while I've never used redux, I think that the most important thing about it is implementing flux: an event driven architecture in the front-end that separate logic and ensure separation of concerns.

I don't know how to implement that with Apollo. The doc says

When mutation modifies multiple entities, or if it creates or deletes entities, the Apollo Client cache is not automatically updated to reflect the result of the mutation. To resolve this, your call to useMutation can include an update function.

Adding an update function in one part of the application that handle all cache updates; by updating queries and/or fragments for the all application, is exactly what we want to avoid in Flux / Event driven architecture.

To illustrate this, let me give a single simple example. Here, we have (at least 3 linked components)

1. InboxCount
Component that show the number of Inbox items in SideNav

query getInboxCount {
    inbox {
        id
        count
    }
}

2. Inbox list items
Component that displays items in Inbox page

query getInbox {
    inbox {
        id
        items {
            ...ItemPreview
            ...ItemDetail
        }
    }
}

Both of those components read data from those GQL queries from auto generated hooks ie. const { data, loading } = useGetInboxItemsQuery()

3. AddItem
Component that creates a new item. Because it creates a new entity I need to manually update cache. So I am forced to write

(pseudo-code)

    const [addItem, { loading }] = useCreateItemMutation({
        update(cache, { data }) {
            const cachedData = cache.readQuery<GetInboxItemsQuery>({
                query: GetInboxItemsDocument,
            })

            if (cachedData?.inbox) {

                // 1. Update items list GetInboxItemsQuery
                const newItems = cachedData.inbox.items.concat(data.items)
                cache.writeQuery({
                    query: GetInboxItemsDocument,
                    data: {
                        inbox: {
                            id: 'me',
                            __typename: 'Inbox',
                            items: newItems,
                        },
                    },
                })

               // 2. Update another query wrapped into another reusable method, here
                setInboxCount(cache, newItems.length)
            }
        },
    })

Here, my AddItem component must be aware of my different other queries / fragments declared in my application ๐Ÿ˜ญMoreover, as it's quite verbose, complexity is increasing very fast in update method. Especially when multiple list / queries should be updated like here

Does anyone have recommendations about implementing a more independent components? Am I wrong with how I created my queries?

Fix layout issues on Firefox

On Firefox 68.0.2, the cards aren't properly displayed when clicking "See More":

image

Also, clicking "See More" on the top section also expands the bottom section, adding a blank space:

image

image

Auth0 config help?

Awesome project! Thanks for sharing ๐Ÿ™ .

I'm attempting to configure a local setup with my own Auth0 account, but have run into a few head scratchers.

Would you please describe how you configured your app and API in Auth0, including any special roles, permissions, etc? This might be something to add to the readme documentation, as well ๐Ÿคท ?

Also, I noticed a few places where 'tottem.app' URLs are hard coded.. Any reasons these don't reference env variables?

Thanks!
Ethan

Tabs component

We should have tabs component styled as specified in figma files

image

Tabs name should be horizontally scrollable on small screen

We should define behaviour on large and small screen when too many sections are set to fill only one line. I don't expect this on large screen soon, but on small screen is almost already an issue. I suggest that the tab names should be horizontally scrollable

Screenshot 2019-09-20 at 17 59 14

Implement multi-profiles feature

For now only one profile is available from static data stored in /data folder. Data structure should be defined in order to access specific profile depending on url

database

What database does this project use?
Postgresql on your server?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.