diff --git a/.erb/configs/webpack.config.main.prod.ts b/.erb/configs/webpack.config.main.prod.ts index d8ab5cd..214ccf9 100644 --- a/.erb/configs/webpack.config.main.prod.ts +++ b/.erb/configs/webpack.config.main.prod.ts @@ -56,7 +56,6 @@ const configuration: webpack.Configuration = { */ new webpack.EnvironmentPlugin({ NODE_ENV: 'production', - DEBUG_PROD: false, START_MINIMIZED: false, }), diff --git a/.eslintrc.js b/.eslintrc.js index f694bf2..f1a5ad7 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -28,6 +28,7 @@ module.exports = { leadingUnderscore: 'allow', }, ], + 'prettier/prettier': 'off', }, parserOptions: { ecmaVersion: 2020, diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 05e3961..1b9e7bd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,6 +20,8 @@ jobs: node-version: 14 - name: Install Packages run: npm install + - name: Add Rollbar + run: echo export default "'${{ secrets.rollbar_token }}'" > ./src/renderer/lib/rollbarAccessToken.ts - name: Build and Test run: npm run build && npm test - name: Lint diff --git a/assets/icon.icns b/assets/icon.icns index 7a5b4f8..0ec58da 100644 Binary files a/assets/icon.icns and b/assets/icon.icns differ diff --git a/package-lock.json b/package-lock.json index e7c6102..253ebee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "version": "0.0.5", + "version": "0.0.12", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1113,9 +1113,9 @@ } }, "@codiga/components": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@codiga/components/-/components-1.1.0.tgz", - "integrity": "sha512-KkzhYI4cBaaiMgK8vkaoa+OQCCYruBGwVTOV9R2ilzMp6bIGr8JR4aWUXcuOJOFFv+FRIrplLW6e9NRlH1dGAQ==" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@codiga/components/-/components-1.3.0.tgz", + "integrity": "sha512-/qTPXcUVWkY30gSIw+wM8RLOTz8Bsyk+2B1CKJKXMAQtUAq4VOYgKkbZIdnlQOqnDIAV/mkSyFocK/UopeLtIw==" }, "@cspotcode/source-map-support": { "version": "0.8.1", @@ -2081,6 +2081,14 @@ "tslib": "^2.1.0" } }, + "@rollbar/react": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/@rollbar/react/-/react-0.11.1.tgz", + "integrity": "sha512-QwI8wPjX1xc/AuX39TSJx/tEtjmf8macRqYgX+R/uRh7Y3+4ilZX9OMwLg/4Je8+NN+9y7PFNKkQa9adn58d/g==", + "requires": { + "tiny-invariant": "^1.1.0" + } + }, "@sinclair/typebox": { "version": "0.24.28", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.28.tgz", @@ -3658,8 +3666,7 @@ "async": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", - "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", - "dev": true + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" }, "async-exit-hook": { "version": "2.0.1", @@ -3929,11 +3936,6 @@ "dev": true, "optional": true }, - "bootstrap": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.1.3.tgz", - "integrity": "sha512-fcQztozJ8jToQWXxVuEyXWW+dSo8AiXWKwiSSrKWsRB/Qt+Ewwza+JWoLKiTuQLaEPhdNAJ7+Dosc9DOIqNy7Q==" - }, "boxen": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/boxen/-/boxen-5.1.2.tgz", @@ -4368,11 +4370,6 @@ "integrity": "sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==", "dev": true }, - "classnames": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.1.tgz", - "integrity": "sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==" - }, "clean-css": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.0.tgz", @@ -4726,6 +4723,11 @@ "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", "dev": true }, + "console-polyfill": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/console-polyfill/-/console-polyfill-0.3.0.tgz", + "integrity": "sha512-w+JSDZS7XML43Xnwo2x5O5vxB0ID7T5BdqDtyqT6uiCAX2kZAgcWxNaGqT97tZfSHzfOcvrfsDAodKcJ3UvnXQ==" + }, "content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -5159,6 +5161,15 @@ "ms": "2.1.2" } }, + "decache": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/decache/-/decache-3.1.0.tgz", + "integrity": "sha512-p7D6wJ5EJFFq1CcF2lu1XeqKFLBob8jRQGNAvFLTsV3CbSKBl3VtliAVlUIGz2i9H6kEFnI2Amaft5ZopIG2Fw==", + "optional": true, + "requires": { + "find": "^0.2.4" + } + }, "decimal.js": { "version": "10.3.1", "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.3.1.tgz", @@ -5195,7 +5206,8 @@ "deepmerge": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==" + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true }, "default-gateway": { "version": "6.0.3", @@ -5464,15 +5476,6 @@ "utila": "~0.4" } }, - "dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "requires": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, "dom-serializer": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.3.2.tgz", @@ -6234,7 +6237,6 @@ "version": "2.0.7", "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.0.7.tgz", "integrity": "sha512-chLOW0ZGRf4s8raLrDxa5sdkvPec5YdvwbFnqJme4rk0rFajP8mPtrDL1+I+CwrQDCjswDA5sREX7jYQDQs9vA==", - "dev": true, "requires": { "stackframe": "^1.1.1" } @@ -7466,6 +7468,15 @@ } } }, + "find": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/find/-/find-0.2.9.tgz", + "integrity": "sha512-7a4/LCiInB9xYMnAUEjLilL9FKclwbwK7VlXw+h5jMvT2TDFeYFCHM24O1XdnC/on/hx8mxVO3FTQkyHZnOghQ==", + "optional": true, + "requires": { + "traverse-chain": "~0.1.0" + } + }, "find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", @@ -7528,15 +7539,6 @@ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "dev": true }, - "frameless-titlebar": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/frameless-titlebar/-/frameless-titlebar-2.1.4.tgz", - "integrity": "sha512-Wz7ZPOLpZNIQg5YxxI2lP6JVrGAolxKN/X85lTOFNpSEe5iJWAKMpYUviuFtc24/Ei8/rBRGkItgdhIFwer7lQ==", - "requires": { - "classnames": "^2.2.6", - "deepmerge": "^4.2.2" - } - }, "framer-motion": { "version": "6.5.1", "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-6.5.1.tgz", @@ -8644,6 +8646,11 @@ "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", "dev": true }, + "is_js": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/is_js/-/is_js-0.9.0.tgz", + "integrity": "sha512-8Y5EHSH+TonfUHX2g3pMJljdbGavg55q4jmHzghJCdqYDbdNROC8uw/YFQwIRCRqRJT1EY3pJefz+kglw+o7sg==" + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -9591,9 +9598,7 @@ "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true, - "optional": true + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" }, "json5": { "version": "2.2.1", @@ -11801,15 +11806,6 @@ "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true }, - "react-popper": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.3.0.tgz", - "integrity": "sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q==", - "requires": { - "react-fast-compare": "^3.0.1", - "warning": "^4.0.2" - } - }, "react-refresh": { "version": "0.14.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.0.tgz", @@ -11900,30 +11896,6 @@ } } }, - "react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "requires": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - } - }, - "reactstrap": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/reactstrap/-/reactstrap-9.1.3.tgz", - "integrity": "sha512-1bYd6JxdnSn5nvvGaimFIAm1EZx79fCJp490/07Mh9B/lwG3/U3hAVOeRKYtYhtaZPgnAIkwxX9sc6G5J9OKuA==", - "requires": { - "@babel/runtime": "^7.12.5", - "@popperjs/core": "^2.6.0", - "classnames": "^2.2.3", - "prop-types": "^15.5.8", - "react-popper": "^2.2.4", - "react-transition-group": "^4.4.2" - } - }, "read-config-file": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.2.0.tgz", @@ -12121,6 +12093,14 @@ "strip-ansi": "^6.0.1" } }, + "request-ip": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/request-ip/-/request-ip-2.0.2.tgz", + "integrity": "sha512-Y6LxqTmxLKKDk2I5tU2sxoCSKAnWJ42jmGqixNrH+oYoAyncpal7fFF5gqJ2bbgkRmb9qYNxdD6KFHfLS4dKBA==", + "requires": { + "is_js": "^0.9.0" + } + }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -12243,6 +12223,33 @@ "sprintf-js": "^1.1.2" } }, + "rollbar": { + "version": "2.25.1", + "resolved": "https://registry.npmjs.org/rollbar/-/rollbar-2.25.1.tgz", + "integrity": "sha512-xDUZqktjtnI/Vv530C5lQKPR8SR8tdlAs0/OeiyBNdfqnL/ngpTHmmrYhgFn4rpEXcs+OzYp3jLquaozBlaCgg==", + "requires": { + "async": "~3.2.3", + "console-polyfill": "0.3.0", + "decache": "^3.0.5", + "error-stack-parser": "^2.0.4", + "json-stringify-safe": "~5.0.0", + "lru-cache": "~2.2.1", + "request-ip": "~2.0.1", + "source-map": "^0.5.7" + }, + "dependencies": { + "lru-cache": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.2.4.tgz", + "integrity": "sha512-Q5pAgXs+WEAfoEdw2qKQhNFFhMoFMTYqRVKKUMnzuiR7oKFHS7fWo848cPcTKw+4j/IdN17NyzdhVKgabFV0EA==" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==" + } + } + }, "run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -12829,8 +12836,7 @@ "stackframe": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.2.1.tgz", - "integrity": "sha512-h88QkzREN/hy8eRdyNhhsO7RSJ5oyTqxxmmn0dzBIMUclZsjpfmrsg81vp8mjjAs2vAZ72nyWxRUwSwmh0e4xg==", - "dev": true + "integrity": "sha512-h88QkzREN/hy8eRdyNhhsO7RSJ5oyTqxxmmn0dzBIMUclZsjpfmrsg81vp8mjjAs2vAZ72nyWxRUwSwmh0e4xg==" }, "stat-mode": { "version": "1.0.0", @@ -13304,6 +13310,12 @@ "punycode": "^2.1.1" } }, + "traverse-chain": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/traverse-chain/-/traverse-chain-0.1.0.tgz", + "integrity": "sha512-up6Yvai4PYKhpNp5PkYtx50m3KbwQrqDwbuZP/ItyL64YEWHAvH6Md83LFLV/GRSk/BoUVwwgUzX6SOQSbsfAg==", + "optional": true + }, "tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", diff --git a/package.json b/package.json index 8526fd0..1e3d2a6 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "coding-assistant", "codiga" ], - "version": "0.0.5", + "version": "0.0.12", "homepage": "https://www.codiga.io/", "bugs": { "url": "https://github.com/codiga/code-snippets-manager/issues" @@ -100,19 +100,18 @@ "@apollo/client": "^3.6.9", "@chakra-ui/icons": "^1.1.7", "@chakra-ui/react": "^1.8.8", - "@codiga/components": "^1.1.0", + "@codiga/components": "^1.3.0", "@electron/remote": "^2.0.8", "@emotion/react": "^11.10.0", "@emotion/styled": "^11.10.0", + "@rollbar/react": "^0.11.1", "apollo-link-debounce": "^3.0.0", - "bootstrap": "^5.1.3", "buffer": "^6.0.3", "cross-fetch": "^3.1.5", "electron-debug": "^3.2.0", "electron-log": "^4.4.8", "electron-store": "^8.1.0", "electron-updater": "^5.2.1", - "frameless-titlebar": "^2.1.4", "framer-motion": "^6.5.1", "graphql": "^16.6.0", "path-browserify": "^1.0.1", @@ -121,7 +120,7 @@ "react-dom": "^18.2.0", "react-hook-form": "^7.34.2", "react-router-dom": "^6.3.0", - "reactstrap": "^9.1.3", + "rollbar": "^2.25.1", "url": "^0.11.0" }, "devDependencies": { @@ -234,12 +233,7 @@ }, "win": { "target": [ - { - "target": "nsis" - }, - { - "target": "msi" - } + "nsis" ], "publisherName": "Xcoding Labs Inc.", "signAndEditExecutable": true, diff --git a/release/app/package-lock.json b/release/app/package-lock.json index a21b76d..7a89f37 100644 --- a/release/app/package-lock.json +++ b/release/app/package-lock.json @@ -1,6 +1,6 @@ { "name": "codiga", - "version": "0.0.5", + "version": "0.0.12", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/release/app/package.json b/release/app/package.json index 76b7cc8..72fa9a8 100644 --- a/release/app/package.json +++ b/release/app/package.json @@ -1,6 +1,6 @@ { "name": "codiga", - "version": "0.0.5", + "version": "0.0.12", "description": "Codiga Code Snippets Manager", "license": "MIT", "author": { diff --git a/src/main/main.ts b/src/main/main.ts index 95b6345..ba28e33 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -31,6 +31,10 @@ ipcMain.on('ipc-example', async (event, arg) => { event.reply('ipc-example', msgTemplate('pong')); }); +ipcMain.on('app-version', async (event) => { + event.reply('app-version', app.getVersion()); +}); + if (process.env.NODE_ENV === 'production') { const sourceMapSupport = require('source-map-support'); sourceMapSupport.install(); @@ -75,8 +79,8 @@ const createWindow = async () => { frame: false, width: 1024, height: 728, - minWidth: 600, - minHeight: 300, + minWidth: 900, + minHeight: 600, icon: getAssetPath('icon.png'), webPreferences: { sandbox: false, @@ -123,6 +127,9 @@ const createWindow = async () => { * Add event listeners... */ +// used for win32 desktop notifications - replaces `electron.app.${APP_NAME}` with Codiga. +app.setAppUserModelId('Codiga'); + app.on('window-all-closed', () => { // Respect the OSX convention of having the application in memory even // after all windows have been closed diff --git a/src/main/preload.ts b/src/main/preload.ts index ccdddf6..6c4609e 100644 --- a/src/main/preload.ts +++ b/src/main/preload.ts @@ -2,6 +2,7 @@ import { contextBridge, ipcRenderer, IpcRendererEvent } from 'electron'; export type Channels = | 'ipc-example' + | 'app-version' | 'minimizeApp' | 'maximizeApp' | 'closeApp'; diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 19bf9a5..e947a55 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -1,7 +1,8 @@ import { ApolloProvider } from '@apollo/client'; -import { ChakraProvider } from '@chakra-ui/react'; +import { ChakraProvider, ColorModeScript } from '@chakra-ui/react'; import { theme } from '@codiga/components'; import { MemoryRouter as Router, Routes, Route } from 'react-router-dom'; +import { Provider as RollbarProvider, ErrorBoundary } from '@rollbar/react'; // PAGES import Home from './pages/Home'; @@ -11,6 +12,8 @@ import MyCookbooks from './pages/MyCookbooks'; import MySnippets from './pages/MySnippets'; import TeamCookbooks from './pages/TeamCookbooks'; import TeamSnippets from './pages/TeamSnippets'; +import ViewSnippet from './pages/ViewSnippet'; +import ViewCookbookSnippets from './pages/ViewCookbookSnippets'; // STYLES import './styles/reboot.css'; @@ -19,58 +22,68 @@ import './styles/app.css'; // OTHER import client from './graphql/client'; +import { rollbarConfig } from './lib/rollbar'; import Layout from './components/Layout'; import Filters from './components/Filters/Filters'; import { UserProvider } from './components/UserContext'; import { ThemeProvider } from './components/ThemeContext'; import { FiltersProvider } from './components/FiltersContext'; -import ViewSnippet from './pages/ViewSnippet'; -import ViewCookbookSnippets from './pages/ViewCookbookSnippets'; export default function App() { return ( - - - - - - - - - }> - } /> - } /> - } - /> - } /> - } - /> - } /> - } - /> - + + + + + + + + + + + + }> + } /> + } /> + } + /> + } + /> + } + /> + } + /> + } + /> + - } - /> - } - /> - - - - - - - - + } + /> + } + /> + + + + + + + + + + ); } diff --git a/src/renderer/components/AboutApp/AboutApp.tsx b/src/renderer/components/AboutApp/AboutApp.tsx new file mode 100644 index 0000000..9cca87e --- /dev/null +++ b/src/renderer/components/AboutApp/AboutApp.tsx @@ -0,0 +1,101 @@ +import { useEffect, useState } from 'react'; +import { + Modal, + ModalOverlay, + ModalContent, + ModalHeader, + ModalCloseButton, + ModalBody, + Text, + Link, + IconButton, + useDisclosure, + Image, +} from '@chakra-ui/react'; +import { QuestionMarkCircleIcon } from '@codiga/components'; +import CodigaLogo from '../Layout/CodigaIcon.png'; + +export default function AboutApp() { + const { isOpen, onOpen, onClose } = useDisclosure(); + const [appVersion, setAppVersion] = useState('0.0.0'); + + useEffect(() => { + // eslint-disable-next-line no-alert + window.electron?.ipcRenderer.once('app-version', (arg) => { + setAppVersion(arg as string); + }); + window.electron?.ipcRenderer.sendMessage('app-version', ['']); + }, []); + + return ( + <> + } + _focus={{ + boxShadow: 'none', + }} + /> + + + + + + + + + + + + + + + + + Codiga Code Snippets Manager + + + + + + Version {appVersion} + + + + Copyright © 2022 Codiga + + + + + ); +} diff --git a/src/renderer/components/AboutApp/index.tsx b/src/renderer/components/AboutApp/index.tsx new file mode 100644 index 0000000..5e7503e --- /dev/null +++ b/src/renderer/components/AboutApp/index.tsx @@ -0,0 +1 @@ +export { default } from './AboutApp'; diff --git a/src/renderer/components/Code/Code.tsx b/src/renderer/components/Code/Code.tsx index b045008..2c1f96e 100644 --- a/src/renderer/components/Code/Code.tsx +++ b/src/renderer/components/Code/Code.tsx @@ -9,7 +9,6 @@ import { Tooltip, Text, Link, - Box, Menu, MenuButton, Portal, @@ -67,169 +66,158 @@ export default function Code({ recipe }: CodeProps) { const minWidth = lineMaxDigits < 3 ? '2.7em' : `${lineMaxDigits}.25em`; return ( - - + - setCodeView(value as CodeViewsType), }} - > - span:first-child > .linenumber:first-child': - { - paddingTop: '0.5em !important', - }, - 'code[class*="language-"] .linenumber': { - border: '0 !important', - background: 'transparent !important', - fontStyle: 'normal !important', - }, - }} - > - - setCodeView(value as CodeViewsType), - }} - /> + /> + + + } + onClick={onCopy} + aria-label="Copy Snippet" + /> + - - } - onClick={onCopy} - aria-label="Copy Snippet" - /> - + + + + + {commentsCount} + + + } + aria-label="Comment on Snippet" + /> + - - + + ••• + + + + - - - {commentsCount} - - - } - aria-label="Comment on Snippet" - /> - - - {userId && recipe.owner && userId === recipe.owner.id && ( - - + - ••• - - - - - - Edit Snippet - - - - - - )} - + Edit Snippet + + + + + + )} + - - {code} - - - - + + span:first-child > .linenumber:first-child': + { + paddingTop: '0.5em !important', + }, + 'code[class*="language-"] .linenumber': { + border: '0 !important', + background: 'transparent !important', + fontStyle: 'normal !important', + }, + }} + > + + {code} + + + ); } diff --git a/src/renderer/components/Favorite/FavoriteCookbook.tsx b/src/renderer/components/Favorite/FavoriteCookbook.tsx index 81165a9..5a17f0d 100644 --- a/src/renderer/components/Favorite/FavoriteCookbook.tsx +++ b/src/renderer/components/Favorite/FavoriteCookbook.tsx @@ -1,5 +1,8 @@ import { useMutation } from '@apollo/client'; import { useToast } from '@codiga/components'; +import { useRollbar } from '@rollbar/react'; +import { LogArgument } from 'rollbar'; + import useQueryVariables from '../../hooks/useQueryVariables'; import { SUBSCRIBE_TO_COOKBOOK, @@ -11,6 +14,7 @@ import { GET_USER_SUBSCRIBED_COOKBOOKS, } from '../../graphql/queries'; import Favorite, { FavoriteProps } from './Favorite'; +import { useUser } from '../UserContext'; type FavoriteCookbookProps = Pick & { cookbookId: number; @@ -21,6 +25,8 @@ export default function FavoriteCookbook({ cookbookId, }: FavoriteCookbookProps) { const toast = useToast(); + const rollbar = useRollbar(); + const { id: userId } = useUser(); const [favoriteCookbook] = useMutation(SUBSCRIBE_TO_COOKBOOK); const [unfavoriteCookbook] = useMutation(UNSUBSCRIBE_TO_COOKBOOK); @@ -56,6 +62,11 @@ export default function FavoriteCookbook({ refetchQueries, }); } catch (err) { + rollbar.error('Error favoriting cookbook', err as LogArgument, { + cookbookId, + isSubscribed, + userId, + }); toast({ status: 'error', description: 'An error occurred. Please try again.', @@ -72,6 +83,11 @@ export default function FavoriteCookbook({ refetchQueries, }); } catch (err) { + rollbar.error('Error unfavoriting cookbook', err as LogArgument, { + cookbookId, + isSubscribed, + userId, + }); toast({ status: 'error', description: 'An error occurred. Please try again.', diff --git a/src/renderer/components/Favorite/FavoriteSnippet.tsx b/src/renderer/components/Favorite/FavoriteSnippet.tsx index 4f24ea4..cd8fbe2 100644 --- a/src/renderer/components/Favorite/FavoriteSnippet.tsx +++ b/src/renderer/components/Favorite/FavoriteSnippet.tsx @@ -1,5 +1,8 @@ import { useMutation } from '@apollo/client'; import { useToast } from '@codiga/components'; +import { useRollbar } from '@rollbar/react'; +import { LogArgument } from 'rollbar'; + import useQueryVariables from '../../hooks/useQueryVariables'; import { SUBSCRIBE_TO_RECIPE, @@ -12,6 +15,7 @@ import { GET_USER_SUBSCRIBED_RECIPES, } from '../../graphql/queries'; import Favorite, { FavoriteProps } from './Favorite'; +import { useUser } from '../UserContext'; type FavoriteRecipeProps = Pick & { recipeId: number; @@ -22,6 +26,8 @@ export default function FavoriteSnippet({ recipeId, }: FavoriteRecipeProps) { const toast = useToast(); + const rollbar = useRollbar(); + const { id: userId } = useUser(); const [favoriteRecipe] = useMutation(SUBSCRIBE_TO_RECIPE); const [unfavoriteRecipe] = useMutation(UNSUBSCRIBE_TO_RECIPE); @@ -63,6 +69,11 @@ export default function FavoriteSnippet({ refetchQueries, }); } catch (err) { + rollbar.error('Error favoriting snippet', err as LogArgument, { + recipeId, + isSubscribed, + userId, + }); toast({ status: 'error', description: 'An error occurred. Please try again.', @@ -79,6 +90,11 @@ export default function FavoriteSnippet({ refetchQueries, }); } catch (err) { + rollbar.error('Error unfavoriting snippet', err as LogArgument, { + recipeId, + isSubscribed, + userId, + }); toast({ status: 'error', description: 'An error occurred. Please try again.', diff --git a/src/renderer/components/Layout/Titlebar.tsx b/src/renderer/components/Layout/Titlebar.tsx index e9c683a..5507765 100644 --- a/src/renderer/components/Layout/Titlebar.tsx +++ b/src/renderer/components/Layout/Titlebar.tsx @@ -10,6 +10,7 @@ import { getAvatarUrl } from '../../utils/userUtils'; import TitlebarButtonsMac from './TitlebarButtonsMac'; import TitlebarButtonsWin from './TitlebarButtonsWin'; +import AboutApp from '../AboutApp'; type TitlebarProps = { openLoginModal: () => void; @@ -38,6 +39,8 @@ export default function Titlebar({ openLoginModal, isOnline }: TitlebarProps) { {/* we show windows buttons, connecting text or CTA/Avatar here */} + + {isOnline ? ( <> ; @@ -14,24 +29,65 @@ export default function TitlebarActionsMac() { ml="space_16" sx={{ '-webkit-app-region': 'no-drag' }} > - - + + + + + + + - + + + + + + - + + + + + + + + + ); diff --git a/src/renderer/components/Login/Login.tsx b/src/renderer/components/Login/Login.tsx index e6d00de..fb165c5 100644 --- a/src/renderer/components/Login/Login.tsx +++ b/src/renderer/components/Login/Login.tsx @@ -14,6 +14,8 @@ import { import { TextField, useToast } from '@codiga/components'; import { useLazyQuery } from '@apollo/client'; import { useForm } from 'react-hook-form'; +import { useRollbar } from '@rollbar/react'; +import { LogArgument } from 'rollbar'; import { APP_URL, TOKEN } from '../../lib/config'; import { CHECK_USER } from '../../graphql/queries'; @@ -26,6 +28,7 @@ type LoginProps = { export default function Login({ isOpen, closeModal }: LoginProps) { const toast = useToast(); + const rollbar = useRollbar(); const { setUser } = useUser(); const { @@ -62,6 +65,7 @@ export default function Login({ isOpen, closeModal }: LoginProps) { }); } } catch (err) { + rollbar.error('Error logging in', err as LogArgument); // network errors while fetching are placed here toast({ status: 'error', diff --git a/src/renderer/components/SnippetResults/SnippetResults.tsx b/src/renderer/components/SnippetResults/SnippetResults.tsx index 69f5ed1..40eeb71 100644 --- a/src/renderer/components/SnippetResults/SnippetResults.tsx +++ b/src/renderer/components/SnippetResults/SnippetResults.tsx @@ -13,9 +13,15 @@ export default function SnippetResults({ results }: SnippetResultsProps) { const query = useUrlQuery(); const currentSnippetId = query.get('currentSnippetId'); - const currentSnippet = currentSnippetId + // if we have a snippet id in the url, search for that first + let currentSnippet = currentSnippetId ? results.find((recipe) => String(recipe.id) === currentSnippetId) - : results[0] || {}; + : undefined; + + // if we can't find the snippet from the url or it wasn't given, use the first result + if (!currentSnippet) { + currentSnippet = results[0] || {}; + } return ( diff --git a/src/renderer/components/ThemeContext/ThemeContext.tsx b/src/renderer/components/ThemeContext/ThemeContext.tsx index 4085609..02a851f 100644 --- a/src/renderer/components/ThemeContext/ThemeContext.tsx +++ b/src/renderer/components/ThemeContext/ThemeContext.tsx @@ -1,22 +1,7 @@ import { useContext, createContext, ReactNode, useEffect } from 'react'; -import { useMutation, useQuery } from '@apollo/client'; import { useColorMode } from '@chakra-ui/react'; -import { useToast } from '@codiga/components'; - -import { useUser } from '../UserContext'; -import { User } from '../../types/userTypes'; -import { GET_USER_PREFERENCES } from '../../graphql/queries'; -import { - RemoveUserPreferenceData, - RemoveUserPreferenceVariables, - REMOVE_USER_PREFERENCE, - UpdateUserPreferenceData, - UpdateUserPreferenceVariables, - UPDATE_USER_PREFERENCE, -} from '../../graphql/mutations'; import useLocalStorage from '../../hooks/useLocalStorage'; import { CODIGA_THEME } from '../../lib/config'; -import { UserPreferenceKey } from '../../lib/constants'; enum Theme { THEME_DARK = 'theme-dark', @@ -29,16 +14,13 @@ type ThemeType = Theme.THEME_DARK | Theme.THEME_LIGHT; type CacheStorageThemeType = (theme: ThemeType) => void; type ThemeContextType = { - changeToDarkTheme: () => Promise; - changeToLightTheme: () => Promise; + changeToDarkTheme: () => void; + changeToLightTheme: () => void; }; const ThemeContext = createContext({} as ThemeContextType); export const ThemeProvider = ({ children }: { children: ReactNode }) => { - const toast = useToast(); - const { id: userId } = useUser(); - // CHAKRA'S THEME const { setColorMode } = useColorMode(); // USER'S LOCAL THEME PREFERENCE @@ -47,25 +29,6 @@ export const ThemeProvider = ({ children }: { children: ReactNode }) => { Theme.THEME_LIGHT ) as [ThemeType, CacheStorageThemeType, () => string]; - /** - * If the user is logged in, we check if they have a saved theme preference. - * If they do, we'll update Chakra and the local version - */ - useQuery<{ user: Partial }>(GET_USER_PREFERENCES, { - skip: !!userId, - onCompleted: (respData) => { - if (respData?.user?.preferences) { - const savedTheme = respData.user.preferences - .map((preference) => preference.key) - .includes(UserPreferenceKey.Theme) - ? Theme.THEME_DARK - : Theme.THEME_LIGHT; - cacheStorageTheme(savedTheme); - setColorMode(savedTheme === Theme.THEME_DARK ? 'dark' : 'light'); - } - }, - }); - /** * Because a user can access the app while not authenticated, * we need to update the theme based on locally stored settings @@ -76,57 +39,14 @@ export const ThemeProvider = ({ children }: { children: ReactNode }) => { setColorMode(storedTheme === Theme.THEME_DARK ? 'dark' : 'light'); }, [cacheStorageTheme, hydrateValue, setColorMode]); - // used to set a DARK theme - const [removeUserPreference] = useMutation< - RemoveUserPreferenceData, - RemoveUserPreferenceVariables - >(REMOVE_USER_PREFERENCE); - - // used to set a LIGHT theme - const [updateUserPreference] = useMutation< - UpdateUserPreferenceData, - UpdateUserPreferenceVariables - >(UPDATE_USER_PREFERENCE); - - const changeToDarkTheme = async () => { + const changeToDarkTheme = () => { cacheStorageTheme(Theme.THEME_DARK); setColorMode('dark'); - if (!userId) return; - try { - await updateUserPreference({ - variables: { - key: UserPreferenceKey.Theme, - value: Theme.THEME_DARK, - }, - refetchQueries: [{ query: GET_USER_PREFERENCES }], - }); - } catch (err) { - toast({ - status: 'error', - description: - 'An error occurred while updating your theme. Please try again.', - }); - } }; - const changeToLightTheme = async () => { + const changeToLightTheme = () => { cacheStorageTheme(Theme.THEME_LIGHT); setColorMode('light'); - if (!userId) return; - try { - await removeUserPreference({ - variables: { - key: UserPreferenceKey.Theme, - }, - refetchQueries: [{ query: GET_USER_PREFERENCES }], - }); - } catch (err) { - toast({ - status: 'error', - description: - 'An error occurred while updating your theme. Please try again.', - }); - } }; const themeContext = { diff --git a/src/renderer/components/Votes/Votes.tsx b/src/renderer/components/Votes/Votes.tsx index 3e35fa7..277218e 100644 --- a/src/renderer/components/Votes/Votes.tsx +++ b/src/renderer/components/Votes/Votes.tsx @@ -3,6 +3,8 @@ import { useInView } from 'framer-motion'; import { useMutation, useQuery } from '@apollo/client'; import { Flex, FlexProps, IconButton, Text, Tooltip } from '@chakra-ui/react'; import { DownVoteIcon, UpVoteIcon, useToast } from '@codiga/components'; +import { LogArgument } from 'rollbar'; +import { useRollbar } from '@rollbar/react'; import { useUser } from '../UserContext'; import { @@ -29,6 +31,7 @@ export default function Votes({ ...props }: VotesProps) { const toast = useToast(); + const rollbar = useRollbar(); const { id: userId } = useUser(); const ref = useRef(null); @@ -65,6 +68,11 @@ export default function Votes({ description: 'Snippet upvoted', }); } catch (err) { + rollbar.error('Error upvoting', err as LogArgument, { + userId, + entityId, + entityType, + }); toast({ status: 'error', description: 'An error occured while upvoting. Please refresh.', @@ -85,6 +93,11 @@ export default function Votes({ description: 'Snippet downvoted.', }); } catch (err) { + rollbar.error('Error downvoting', err as LogArgument, { + userId, + entityId, + entityType, + }); toast({ status: 'error', description: 'An error occured while downvoting. Please refresh.', diff --git a/src/renderer/lib/constants.ts b/src/renderer/lib/constants.ts index 2792938..c484474 100644 --- a/src/renderer/lib/constants.ts +++ b/src/renderer/lib/constants.ts @@ -8,11 +8,14 @@ export enum Language { LANGUAGE_JSON = 'Json', LANGUAGE_YAML = 'Yaml', LANGUAGE_TYPESCRIPT = 'Typescript', + LANGUAGE_TWIG = 'Twig', LANGUAGE_SWIFT = 'Swift', LANGUAGE_SOLIDITY = 'Solidity', LANGUAGE_SQL = 'Sql', LANGUAGE_SHELL = 'Shell', LANGUAGE_SCALA = 'Scala', + LANGUAGE_SCSS = 'Scss', + LANGUAGE_SASS = 'Sass', LANGUAGE_REACT = 'React', LANGUAGE_PASCAL = 'Pascal', LANGUAGE_RUST = 'Rust', @@ -20,6 +23,7 @@ export enum Language { LANGUAGE_PHP = 'Php', LANGUAGE_PYTHON = 'Python', LANGUAGE_PERL = 'Perl', + LANGUAGE_MARKDOWN = 'Markdown', LANGUAGE_KOTLIN = 'Kotlin', LANGUAGE_JAVASCRIPT = 'Javascript', LANGUAGE_JAVA = 'Java', diff --git a/src/renderer/lib/rollbar.ts b/src/renderer/lib/rollbar.ts new file mode 100644 index 0000000..ac7e219 --- /dev/null +++ b/src/renderer/lib/rollbar.ts @@ -0,0 +1,20 @@ +import rollbarAccessToken from './rollbarAccessToken'; + +export const rollbarConfig = { + accessToken: rollbarAccessToken, + captureUncaught: true, + captureUnhandledRejections: true, + payload: { environment: process.env.NODE_ENV || 'development' }, + autoInstrument: { + network: true, + networkResponseHeaders: true, + // networkResponseBody: true, + networkRequestBody: true, + log: true, + dom: true, + navigation: true, + connectivity: true, + contentSecurityPolicy: true, + errorOnContentSecurityPolicy: true, + }, +}; diff --git a/src/renderer/lib/rollbarAccessToken.ts b/src/renderer/lib/rollbarAccessToken.ts new file mode 100644 index 0000000..f546b82 --- /dev/null +++ b/src/renderer/lib/rollbarAccessToken.ts @@ -0,0 +1 @@ +export default 'ROLLBAR_TOKEN';