{"id":21633765,"url":"https://github.com/dgroomes/my-github-explorer","last_synced_at":"2026-04-12T14:37:55.357Z","repository":{"id":214888033,"uuid":"714738071","full_name":"dgroomes/my-github-explorer","owner":"dgroomes","description":"⏯ A toy GitHub data explorer which I'm using to explore frontend tech like GraphiQL, React hooks, and Redux.","archived":false,"fork":false,"pushed_at":"2024-05-19T15:45:50.000Z","size":1018,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-01-25T00:14:20.237Z","etag":null,"topics":["electron","github-api","graphiql","graphql"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dgroomes.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2023-11-05T18:11:17.000Z","updated_at":"2023-12-31T21:54:28.000Z","dependencies_parsed_at":"2024-03-02T00:44:38.501Z","dependency_job_id":null,"html_url":"https://github.com/dgroomes/my-github-explorer","commit_stats":null,"previous_names":["dgroomes/my-github-explorer"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dgroomes%2Fmy-github-explorer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dgroomes%2Fmy-github-explorer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dgroomes%2Fmy-github-explorer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dgroomes%2Fmy-github-explorer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dgroomes","download_url":"https://codeload.github.com/dgroomes/my-github-explorer/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244320177,"owners_count":20434088,"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":["electron","github-api","graphiql","graphql"],"created_at":"2024-11-25T03:13:48.013Z","updated_at":"2025-12-31T00:13:08.376Z","avatar_url":"https://github.com/dgroomes.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# my-github-explorer\n\nNOT YET FULLY IMPLEMENTED\n\n⏯️ A toy GitHub data explorer which I'm using to explore frontend tech like GraphiQL, Redux, React, TypeScript and Electron.\n\n\n## Overview\n\nThis is a mash-up of front-end tech I'm interested in learning more deeply by way of a realistic application. I often\nlearn libraries, languages and frameworks by way of minimal runnable examples, but these small examples don't let\ncomplex integration scenarios emerge. This project is an SPA that uses the GitHub GraphQL API and [GraphiQL](https://github.com/graphql/graphiql)\ncomponents to fetch metadata about repositories and present it using React. It also uses Redux and Electron. The amount\nof side-effecting, stateful code and UI complexity is enough to really showcase how this tech works.\n\nThis is not an endorsement for any of these technologies.\n\n\n## Instructions\n\nFollow these instructions to build and serve the application:\n\n1. Pre-requisite: Node.js\n    * I used the version of Node specified in the `.nvmrc` file. If you are using `nvm` then you can run the following\n      command to use the correct version of Node.\n    * ```shell\n      nvm use\n      ```\n2. Pre-requisite: `build-support`\n    * **Important**: The `build-support` library must be built before you can develop the main application. Follow the\n      instructions in the [`build-support` README](build-support/README.md). You only need to do this the first time and\n      then any time you change `build-support`. Re-install it with the following command.\n    * ```shell\n      npm install --save-dev ./build-support/my-github-explorer_build-support-1.0.0.tgz\n      ```\n3. Install dependencies\n    * ```shell\n      npm install\n      ```\n4. Continuously build and run the app\n    * ```shell\n      npm start\n      ```\n5. Make the app distribution\n    * ```shell\n      npm run make\n      ```\n    * You will then need to find the `.dmg` file in the `out` directory and install it.\n\n\nTip: the GitHub personal access token is saved for convenience in an unencrypted file in a conventional directory in the\nElectron app's installation directory. But you might want to blow this away to force the app through another flow. Use\nthis alias:\n\n```shell\nalias my-github-explorer-reset-token='rm \"$HOME/Library/Application Support/my-github-explorer/pat\"'\n```\n\n\n## Instructions for React and Redux Developer Tools\n\nWhen you develop a React or Redux application, you'll likely want the power of their excellent developer tools: [React Developer Tools](https://react.dev/learn/react-developer-tools)\nand [Redux DevTools](https://github.com/reduxjs/redux-devtools). In fact, using React and Redux come at a price and\nthese tools are essential to offset the cost.\n\nBecause we're in an Electron environment, it's a little tricky to install browser extensions, and it's an especially\ntricky situation in 2024 with the inconsistent support for Manifest v2 and v3. Electron doesn't support Manifest v3 yet\nbut React Developer Tools doesn't support v2 anymore. Tricky. Thankfully, React Developer Tools and Redux DevTools can\neach be run in a *standalone mode* by way of their npm-distributed CLI programs:\n\n* [`react-devtools`](https://github.com/facebook/react/blob/main/packages/react-devtools/README.md)\n* [`@redux-devtools/cli`](https://github.com/reduxjs/redux-devtools/tree/main/packages/redux-devtools-cli)\n\nMy preference is to run these standalone tools. By contrast, I am interested in using [browser extensions in Electron](https://www.electronjs.org/docs/latest/api/extensions),\nbut I'm not ready to figure out the compatibility workarounds (aforementioned v2/v3 quagmire).  \n\nFollows these instructions to install and run the developer tools and connect to them from our app:\n\n1. Install the tools globally:\n    * ```shell\n      npm install -g react-devtools @redux-devtools/cli@3.0.2\n      ```\n2. Run React Developer Tools in standalone mode:\n    * ```shell\n      react-devtools\n      ```\n3. Run a Redux DevTools server, and establish a connection from the web app\n    * ```shell\n      redux-devtools --open=browser --port=8000 --hostname=127.0.0.1\n      ```\n    * This starts the server and opens your web browser to \u003chttp://127.0.0.1:8000\u003e. You need to configure the web app to\n      connect to the server by going to the \"Settings\" tab and then clicking the \"Use local (custom) server\" radio button\n      and clicking the \"Connect\" button. This is a one-time setup because this setting is stored in local storage.\n4. Run our app but with a special flag to connect to the standalone React Developer Tools:\n    * ```shell\n      npm run start:with-devtools\n      ```\n5. Inspect the React components and Redux state.\n    * Go back to the web app that hosts the Redux DevTools UI and **refresh the page** (I don't get why this is necessary).\n    * You'll see the Redux state and state history of the program.\n    * Go to the React Developer Tools Electron window and inspect the React components.\n    * Develop -\u003e Debug -\u003e Develop -\u003e Debug etc...\n\n\n## Wish List\n\nGeneral clean-ups, todos and things I wish to implement for this project:\n\n* [ ] Query (search) for the current user's repositories. Hardcode to a 100 limit (which is a limit of the API; then\n  you'll need pagination)\n* [ ] Query (get) the metadata for each of the repositories\n* [ ] Display the information in a table using [React Table](https://react-table-v7.tanstack.com/) \n* [ ] Consider paginating through the results. This would be a cool application because we would see the table size grow.\n* [ ] Show the pre-constructed queries in codemirror (using a GraphiQL component). We want the syntax highlighting. Can\n  we make them read-only?\n* [ ] Consider enforcing `noImplicitAny`\n* [ ] DONE (I'm struggling with the program design; using too much globals) Move more code from `useEffect` to Redux listener callbacks/functions. \n   * Consider removing `useToken` entirely. I'm acutely aware of the \"fetch on render\" (or rather, \"side-effect on render\")\n     program construction. While it's often fine, it's a conflation I think. In these context, David Khourshid's advice\n     really rings true about [saying goodbye to `useEffect`](https://www.youtube.com/watch?v=bGzanfKVFeU).\n   * After pushing logic into `token.ts`, revisit the exported components. We can probably encapsulate more. \n* [ ] RTK 2.0 ready? RTK 2.0 is a big upgrade, and I'm eager to upgrade. But Redux Developer Tools are not compatible with\n  RTK 2.0 yet. Only upgrade when it's compatible. See [more info in my own extensive notes](https://github.com/dgroomes/redux-playground/blob/d2ec267b80831c1576a5db1a9408d828febce1b1/redux-devtools-remote/README.md?plain=1#L88).\n* [ ] Where am I supposed to put Redux-coupled code? In `store.ts`, `-slice.ts`, or somewhere else?\n* [x] DONE I'm bothered by modules that do top-level work. Does the `store.ts` file really need to be the way it is? Why do\n  we use globals so brazenly?\n    * DONE Consider combining more token stuff into the same file. I'm just experimenting here.\n* [ ] Bring back `useAppDispatch` now that I understand utility types better.\n* [x] DONE Rename `app.tsx` to `index.tsx`. I think I like the naming convention of \"index\" for web pages and I guess in an\n  Electron context the word \"main\" is a good convention for the main process.\n\n\n## Finished Wish List Items\n\n* [x] DONE Scaffold the project\n* [x] DONE Prompt for an access token. Validate the access token with a request to the GitHub API. Give\n  informational UI feedback.\n* [x] DONE Query for the current user's login (assuming the token is a user token)\n* [x] DONE (I like it very much) Model the PAT using some kind of types. I want to model the state machine a lot more cleanly than the mess of\n  `useState` and nullability I have now.\n* [x] DONE (I accomplished an abstraction, but it is not great) Abstract the \"useEffect unmount clean up fetch abort\" stuff into generic code. I'm assuming there must be some\n  concise API to derive for this common use case.\n* [x] DONE Abstract the token validation into a hook\n* [x] DONE Extract into its own repository\n* [x] DONE Turn this into an Electron app (primarily because I want native secrets)\n  * This was extremely larger than I anticipated. Went down a rabbit hole here: \u003chttps://github.com/dgroomes/electron-playground/tree/main/realistic\u003e\n    I'll port over the same thing.\n  * DONE Port over `build-support`\n  * DONE Port over `package-json.mjs` style.\n  * DONE Add `main.js` file for Electron main process.\n  * DONE .gitignore\n  * DONE Get it to build and add instructions to README.\n  * DONE tsconfig changes\n  * DONE Get the styles to work. I need to figure out how to import the CSS from GraphiQL again.\n  * Make sure React DevTools works\n* [x] DONE Upgrade dependencies.\n* [x] DONE (GraphiQL defines fonts in data URLs) Why are there HTTP request failures to download fonts? E.g. `data:font/woff2;base64,` etc. This happens when\n  serving but not in the production app.\n* [x] DONE Get token from storage using Electron's `safeStorage` API (well safe is always relative). This involves code across\n  the main and renderer processes. Note: I've been writing hard-to-reason-about state-checking code to infer what work\n  my code needs to take next. This is awkward because we're reconstituting the transition from the state but what would\n  be more natural is if we could just do the work (or queue it up?) at the time we made the state change (i.e. transition)\n  in the first place. For example, I set state to \"storing\" but then at a later invocation of the render function I use\n  a combination of checking for \"storing\" and also `useEffects` dependencies to actually kick off the storing work. But\n  it's like... at this point we're actually doing the storing work. Should I model it as \"need-to-store\" and \"storing\"\n  and just jam descriptive states in the state? I don't really get it, but the nature of an async domain is tricky\n  regardless.\n* [x] DONE Use Redux. I think I want to use Redux Sagas specifically. I don't grok Redux. I need to learn it an app that\n  \"does real things\" like make HTTP requests and handles a rich UI. Hopefully I'll get it. I'm expecting the Redux\n  devtools will be particularly useful, but jury is out.\n   * DONE Start by bringing in the dependencies and maybe doing a \"hello world\".\n   * DONE Port one instance of `setState` to Redux. The lowest level (the leaf node) is where `useEffect` uses `setState`.\n     We'll port over all other usages and the `useEffect` usages in a later task.\n     * DONE Redux wants only serializable data in the state. Makes sense (although profoundly limiting; but\n       constraints can be good). Take only what I need in place of the `Response`\n       object.\n   * DONE Port everything else to Redux (but don't get into Sagas yet)\n* [x] DONE Add Redux DevTools.\n   * Note: I completely misread the docs. I thought there was no standalone launcher for Redux DevTools, but there is.\n     It's called redux-devtools-cil.\n   * DONE Add instructions for running Redux DevTools from its CLI.\n   * DONE Add Redux DevTools client side stuff and make sure it can be connected to from the dev tool instance.\n   * DONE Co-opt the `start:react-devtools` script to also start the Redux DevTools. And figure out [how to exclude\n     the package from the production build](https://github.com/reduxjs/redux-devtools/blob/main/docs/Walkthrough.md#exclude-devtools-from-production-builds). \n* [x] DONE Consider adding Prettier or something. I'm mostly annoyed with arbitrarily using double and single quotes and using\n  and not using semicolons.\n* [x] DONE (fixed, but `useFetch` now doesn't make sense. How do people do this? Just ignore unmounts for the clean up function?)\n      Defect: validation fetch request is getting cancelled prematurely. My `useFetch` must be buggy.\n* [x] DONE Stop setting state from render function. During the redux conversion, I started getting warnings about setting\n  state from the render function. Totally (well 80%) makes sense to me, so I'll fix it. This is part of the process of\n  grokking React. The trouble is in `useToken`. At this time, it's time to drop `useFetch` which I had previously marked\n  as deprecated. It's so hard to make this work.\n* [x] DONE More robust error handling. We at least want to model basic error states and propagate a basic message.\n* [ ] SKIP (I get the gist of this technology/paradigm but I don't see how I can be succesful/effective with it. Maybe on another project) Port to Redux Sagas\n    * This is the real experiment: can we actually figure out how to implement the logic in Sagas?\n* [x] DONE (I used a store listener to solve this; happy with it and what I learned about Redux and RTK) (I need to figure out how to do async stuff using Redux I think. So Sagas or thunk?) Defect. The validation request is doubling. I mean I imagine this is because of React strict mode. How am should I deal\n  with this?\n    * Side note: I've spent about 15 minutes researching React strict mode and I can't find in the official docs, stackoverflow\n      or the source code what is meant by \"strict mode only runs in development mode\". Did the whole world agree that \"dev web pages\"\n      have a global variable named `__DEV__` (or something like that) set to `true`?\n    * I need to not tie the fetch lifecycle to the UI component (I guess this is kind of obvious). I just want to spell it\n      out for myself. Letting the mounting of a component trigger `useEffect` is fine in some cases, but when there is\n      work/state that exists even \"before\" or \"outside\" a UI component then manage that in non-UI code (and to me the \"render\"\n      function, which is a React function component is a UI thing). This is continually a murky subject for me (and most).\n      OR actually, it's just silly to initialize the state to \"restoring\"... I should really do \"init\" or \"untriggered\"\n      (I keep revisiting this same concept...). UPDATE: I've heard the phrase \"fetch on render\" used in the wild and that's\n      a good description.\n    * UPDATE: RTK has  [`createListenerMiddleware`](https://redux-toolkit.js.org/api/createListenerMiddleware). That's what I'll use.\n    * UPDATE: Ok I am going to use a \"kick-off token restoration on render\" strategy. I'm using Redux listeners and state\n      so that React's strict mode double effect-triggering is countered (phew, feels good).\n* [ ] SKIP (I don't want to do this. \"Components encapsulating long-lived state\" is wrong. Components are flighty because of the nature of React. I'm happy with my token state/logic in Redux, or even organic JS) Abstract the token input/storage into a component\n* [ ] OBSOLETE (I think this is not true. I think I am getting all the state. Refreshing the page I think is the important part) Figure out some trick to prevent the app from initializing until Redux DevTools have attached. I don't think I'm getting\n  all the early action history which is a shame. If I can get a comprehensive history, then I can delete some of the log\n  statements.\n\n\n## Reference\n\n* [GitHub GraphQL API](https://docs.github.com/en/graphql)\n* [GitHub docs: *Search within a user's or organization's repositories*](https://docs.github.com/en/search-github/searching-on-github/searching-for-repositories#search-within-a-users-or-organizations-repositories)\n* \u003chttps://github.com/dgroomes/electron-playground\u003e\n* \u003chttps://github.com/dgroomes/react-playground\u003e\n* \u003chttps://github.com/dgroomes/redux-playground\u003e\n* \u003chttps://github.com/dgroomes/graphiql-playground\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdgroomes%2Fmy-github-explorer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdgroomes%2Fmy-github-explorer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdgroomes%2Fmy-github-explorer/lists"}