{"id":13423891,"url":"https://github.com/poulainv/tottem","last_synced_at":"2025-03-15T17:32:39.624Z","repository":{"id":39340375,"uuid":"206781629","full_name":"poulainv/tottem","owner":"poulainv","description":"Bookmark manager on steroid built with React / NextJs / Apollo Tools / Prisma 2 — styled with TailwindCSS 🌱🎺","archived":false,"fork":false,"pushed_at":"2023-01-24T01:15:30.000Z","size":17573,"stargazers_count":979,"open_issues_count":46,"forks_count":99,"subscribers_count":16,"default_branch":"master","last_synced_at":"2024-10-26T23:12:55.754Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://beta.tottem.app","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/poulainv.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-09-06T11:41:29.000Z","updated_at":"2024-10-18T00:36:48.000Z","dependencies_parsed_at":"2023-02-13T08:15:59.595Z","dependency_job_id":null,"html_url":"https://github.com/poulainv/tottem","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/poulainv%2Ftottem","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/poulainv%2Ftottem/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/poulainv%2Ftottem/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/poulainv%2Ftottem/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/poulainv","download_url":"https://codeload.github.com/poulainv/tottem/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243767515,"owners_count":20344934,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T00:00:44.511Z","updated_at":"2025-03-15T17:32:35.006Z","avatar_url":"https://github.com/poulainv.png","language":"TypeScript","funding_links":[],"categories":["Apps","TypeScript","others",":space_invader: Projects Using Prisma"],"sub_categories":["Tutorials with [NestJS](https://nestjs.com/)"],"readme":"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.\n\n\u003cdiv align=\"center\"\u003e\n\n[![Tottem](./public/logo.png)](https://beta.tottem.app)\n\n### Library management made social\n\n\u003c/div\u003e\n\nI have two considerations in mind:\n\n-   building a product based on ethic design\n-   experimenting open-source web technologies and share it\n\n## Summary\n\n-   [Summary](#summary)\n-   [Tech](#tech)\n    -   [Codebase](#codebase)\n        -   [Main technologies](#main-technologies)\n        -   [Repository structure — front-end](#repository-structure--front-end)\n        -   [How is it typesafe from end-to-end?](#how-is-it-typesafe-from-end-to-end)\n        -   [Global state management](#global-state-management)\n    -   [SSR Workflow](#ssr-workflow)\n    -   [Deployment](#deployment)\n    -   [Setup](#setup)\n-   [Product](#product)\n    -   [Why ?](#why)\n    -   [How ?](#how)\n-   [Contributors](#contributors)\n\n\u003cdiv\u003e\n\n[![Tottem](./public/screenshot-section.png)](https://beta.tottem.app)\n\n\u003c/div\u003e\n\n## Tech\n\nFirst 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.\n\n### Codebase\n\n#### Main technologies\n\nIt's a full-stack Typescript app with some code generation in order to have a type safe experience from end-to-end.\n\nHere is a list of main technologies used:\n\n-   🚀 [React](https://github.com/facebook/react)\n-   🥇 [NextJS](https://github.com/zeit/next.js) to provide fast SSR experience\n-   😍 [TailwindCSS](https://github.com/tailwindcss/tailwindcss)\n-   📱 GraphQL, powered by [Apollo tools](https://github.com/apollographql)\n-   👮‍♂️ [Auth0](https://auth0.com/) for authentication\n-   🚓 [Prisma Framework](https://github.com/prisma/prisma2) to manage data model and database\n\n#### Repository structure — front-end\n\nInspired by those [recommendations](https://medium.com/@alexmngn/how-to-better-organize-your-react-applications-2fd3ea1920f1), this is how the codebase is structured:\n\n```sh\ntottem/\n├── api # contains graphlq endpoint based on Apollo Server \u0026 Prisma2\n├──── prisma # contains model definitions \u0026 database migration logbook\n├──── src/schema # contains graphql resolvers using Prisma Client\n├── src\n├──── generated # contains generated code (types, hooks, ...)\n├──── pages # static and dynamic routes declaration used by NextJS\n├──── components # shared generic component\n├──── scenes # different parts of the application\n├────── moduleName # Auth | Profile | Me ...\n├──────── components # module components. **Each** components can specify its own specific components, queries, ...\n├──────── queries.gql # All data queries and mutations are written in gql files\n├──────── hooks.ts # Most of the reusable logic is written in hooks\n├──────── index.tsx # Main scene file\n├──────── View.tsx # Sometime stateless component are isolated in View file for clarity or reusability\n└──── services # shared services as authentication, error management, ...\n```\n\n#### How is it typesafe from end-to-end?\n\n-   **Prisma** provides a [library](https://github.com/prisma/photonjs) Photon that _generate_ a typesafe client to manipulate data depending on a unique schema declaration in `schema.prisma` file.\n\nExample where Photon is used to retrieve the not softly deleted items from specific collection :\n\n```\nconst items = (\n    await ctx.photon.items.findMany({\n        where: {\n            collection: { id: collectionId },\n            isDeleted: false,\n        },\n        select: { id: true, position: true },\n        orderBy: { createdAt: 'desc' },\n    })\n```\n\n-   **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](https://github.com/prisma-labs/nexus-prisma) to automatically declare resolvers based on your photon client.\n\n-   **graphql-codegen** [tool](https://graphql-code-generator.com/) 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.\n\n\u003e Note that when global state management is required, Apollo Client is used as much as possible.\n\nExample where typesafe hook `useGetItemsQuery` is generated allowing to fetch data via Apollo Client smoothly\n\n```\nconst { data } = useGetItemsQuery({\n        variables: {\n            collectionId,\n        },\n    })\n```\n\n🤯 No typo anymore, much less files \u0026 context switching with typescript ✨\n\n#### Global state management\n\nMost 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`\n\n```gql\ntype Breadcrumb {\n    title: String!\n    href: String!\n}\n\nextend type Query {\n    breadcrumbs: [Breadcrumb!]!\n}\n```\n\nThen, we can define\n\n1. **Query** to read data from Apollo cache\n\n```graphql\nquery getBreadcrumbs {\n    breadcrumbs @client {\n        title\n        href\n    }\n}\n```\n\n`codegraphql-code` is configured to generate this simple hook query, used in Profile/TopBar for instance:\n\n```typescript\nconst { data } = useGetBreadcrumbsQuery()\n```\n\n2 **Custom hooks** to write data to cache with Apollo `client`\n\n```typescript\nconst useBreadcrumbs = (profileSlug: string) =\u003e {\n    const client = useApolloClient()\n\n    const setBreadcrumbs = ({ breadcrumbs }: GetCollectionProfileQuery) =\u003e {\n        client.writeData({\n            data: {\n                breadcrumbs,\n            },\n        })\n    }\n\n    const resetBreadcrumbs = () =\u003e {\n        client.writeData({\n            data: {\n                breadcrumbs: [],\n            },\n        })\n    }\n\n    return { resetBreadcrumbs, setBreadcrumbs }\n}\n```\n\n### SSR Workflow\n\nNextJS provides SSR features that make user experience awesome. NextJS comes with the concept of pre-rendering built-in, that can take 2 forms:\n\n-   Static Generation\n-   Server-side rendering\n\nWhen developing an app, pages are usually not static and need to be rendered on-demand depending on the context (ie. user). [NextJS documentation](https://nextjs.org/docs) 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:\n\n[![Tottem](./public/ssr-diagram.png)]()\n\n### Deployment\n\nThe app is fully deployed on Zeit Now. Configuration can be found in `now.json` file. Merging on master trigger new deployment.\n\nThe API runtime is _serverless_ and run via [Now Serverless Functions](https://zeit.co/docs/v2/serverless-functions/introduction).\nAs described [here](https://github.com/poulainv/tottem/pull/113#issuecomment-577685621), API performance with Prisma Client is pretty good even with coldstart.\n\n### Setup\n\nLocally PG instance is needed with some var env set. In `.env` for instance\n\n```sh\nAUTH0_LOCAL_ID='auth0|5dc8800986c8ba0e74d73654' # set local user id by passing auth0\nAUTH0_CALLBACK='http://localhost:3000/auth/callback'\nDATABASE_URL=\"postgresql://XXX@localhost:5432/XXX?sslaccept=accept_invalid_certs\"\nGRAPHQL_URL='http://localhost:4000/graphql'\nDATABASE_PROVIDER=\"postgresql\"\n```\n\nThen, two repositories are needed\n\n```sh\ngit clone git@github.com:poulainv/tottem.git\ncd tottem\nnpm install\nnpm run dev\n```\n\n```sh\ncd tottem\ncd api\nnpm install\nnpm run dev\n```\n\nWeb app is available on `http://localhost:3000` and graphql endpoint on `http://localhost:4000/graphql`\n\n## Product\n\nSecond 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 \u0026 community 😇\n\n\u003cdiv\u003e\n\n[![Tottem](./public/screenshot-inbox.png)](https://beta.tottem.app)\n\n\u003c/div\u003e\n\n### Why ?\n\nMore and more, people — especially journalists, are losing their ability to choose which content to promote \u0026 amplify. Instead, automatic recommendation algorithms carefully choose the _best_ (sic!) content to amplify. As reader, those deep learning algorithms create a unique \u0026 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?\n\n[Here, I'm happy to share some references](https://beta.tottem.app/vincent/c/inspirational-content-about-why-social-media-companies-are-dangerous-for-personal-attention-and-democracy-ck5i4lwp2000vws9e4ry25feh)\n\nSo, 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?\n\n### How ?\n\nTottem aims combining personal productivity tool approach with (slow) social media capabilities to make users empowered and somehow emancipated.\n\nTottem 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.\n\nThe basic workflow:\n\n1. Collect everything in one Inbox.\n2. Organise into Spaces and Collection.\n3. Express yourself and explain your opinion\n4. Publish and share with your community\n\n\u003cdiv\u003e\n\n[![Tottem](./public/screenshot-profile.png)](https://beta.tottem.app)\n\n\u003c/div\u003e\n\n### Design\n\nThe UI/UX design is obviously inspired by [Notion](https://notion.so) and [Things 3](https://culturedcode.com/things/). The design work made with Figma is available [there](https://www.figma.com/file/isqiSo35dfp5oGIpYZlCM6/Tottem-Design?node-id=255%3A1261)\n\n\u003cdiv align=\"center\"\u003e\n\n[![Tottem](./public/screenshot-design.png)](https://www.figma.com/file/isqiSo35dfp5oGIpYZlCM6/Tottem-Design?node-id=255%3A1261)\n\n\u003c/div\u003e\n\n## Contributors\n\n-   Clément Déon [@deonclem](https://github.com/deonclem)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpoulainv%2Ftottem","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpoulainv%2Ftottem","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpoulainv%2Ftottem/lists"}