├── .babelrc ├── .eslintrc.js ├── .gitignore ├── README.md ├── config ├── jest-style-mock.js └── jest.js ├── package.json ├── src ├── _locales │ ├── ca │ │ └── messages.json │ ├── cs │ │ └── messages.json │ ├── da │ │ └── messages.json │ ├── de │ │ └── messages.json │ ├── el │ │ └── messages.json │ ├── en │ │ └── messages.json │ ├── es │ │ └── messages.json │ ├── fi │ │ └── messages.json │ ├── fr │ │ └── messages.json │ ├── he │ │ └── messages.json │ ├── id │ │ └── messages.json │ ├── it │ │ └── messages.json │ ├── locales-to-support.md │ ├── nl │ │ └── messages.json │ ├── no │ │ └── messages.json │ ├── pl │ │ └── messages.json │ ├── pt_BR │ │ └── messages.json │ ├── pt_PT │ │ └── messages.json │ ├── ro │ │ └── messages.json │ ├── ru │ │ └── messages.json │ ├── sv │ │ └── messages.json │ ├── tr │ │ └── messages.json │ ├── uk │ │ └── messages.json │ └── zh-TW │ │ └── messages.json ├── background │ └── index.js ├── icons │ ├── icon-128.png │ ├── icon-16.png │ ├── icon-32.png │ ├── icon-48.png │ ├── icon-active-128.png │ ├── icon-active-16.png │ ├── icon-active-32.png │ └── icon-active-48.png ├── manifest.json ├── options │ ├── components │ │ └── App │ │ │ ├── App.css │ │ │ ├── App.js │ │ │ ├── __tests__ │ │ │ ├── App.test.js │ │ │ └── __snapshots__ │ │ │ │ └── App.test.js.snap │ │ │ ├── components │ │ │ ├── Footer │ │ │ │ ├── Footer.css │ │ │ │ ├── Footer.js │ │ │ │ ├── __tests__ │ │ │ │ │ ├── Footer.test.js │ │ │ │ │ └── __snapshots__ │ │ │ │ │ │ └── Footer.test.js.snap │ │ │ │ └── index.js │ │ │ ├── Logo │ │ │ │ ├── Logo.css │ │ │ │ ├── Logo.js │ │ │ │ ├── __tests__ │ │ │ │ │ ├── Logo.test.js │ │ │ │ │ └── __snapshots__ │ │ │ │ │ │ └── Logo.test.js.snap │ │ │ │ └── index.js │ │ │ ├── Offline │ │ │ │ ├── Offline.js │ │ │ │ ├── __tests__ │ │ │ │ │ ├── Offline.test.js │ │ │ │ │ └── __snapshots__ │ │ │ │ │ │ └── Offline.test.js.snap │ │ │ │ └── index.js │ │ │ └── Online │ │ │ │ ├── Online.js │ │ │ │ ├── __tests__ │ │ │ │ ├── Online.test.js │ │ │ │ └── __snapshots__ │ │ │ │ │ └── Online.test.js.snap │ │ │ │ ├── components │ │ │ │ ├── Login │ │ │ │ │ ├── Login.js │ │ │ │ │ ├── __tests__ │ │ │ │ │ │ ├── Login.test.js │ │ │ │ │ │ └── __snapshots__ │ │ │ │ │ │ │ └── Login.test.js.snap │ │ │ │ │ ├── components │ │ │ │ │ │ └── Loading │ │ │ │ │ │ │ ├── Loading.css │ │ │ │ │ │ │ ├── Loading.js │ │ │ │ │ │ │ └── index.js │ │ │ │ │ └── index.js │ │ │ │ └── Options │ │ │ │ │ ├── Options.js │ │ │ │ │ ├── __tests__ │ │ │ │ │ ├── Options.test.js │ │ │ │ │ └── __snapshots__ │ │ │ │ │ │ └── Options.test.js.snap │ │ │ │ │ └── index.js │ │ │ │ └── index.js │ │ │ └── index.js │ ├── index.html │ ├── index.js │ └── redux │ │ ├── actions │ │ ├── online.js │ │ ├── options.js │ │ └── user.js │ │ └── reducers │ │ ├── index.js │ │ ├── online.js │ │ ├── options.js │ │ └── user.js ├── popup │ ├── components │ │ └── App │ │ │ ├── App.css │ │ │ ├── App.js │ │ │ ├── __tests__ │ │ │ ├── App.test.js │ │ │ └── __snapshots__ │ │ │ │ └── App.test.js.snap │ │ │ ├── components │ │ │ ├── Logo │ │ │ │ ├── Logo.css │ │ │ │ ├── Logo.js │ │ │ │ ├── __tests__ │ │ │ │ │ ├── Logo.test.js │ │ │ │ │ └── __snapshots__ │ │ │ │ │ │ └── Logo.test.js.snap │ │ │ │ └── index.js │ │ │ ├── Main │ │ │ │ ├── Main.js │ │ │ │ ├── __tests__ │ │ │ │ │ ├── Main.test.js │ │ │ │ │ └── __snapshots__ │ │ │ │ │ │ └── Main.test.js.snap │ │ │ │ ├── components │ │ │ │ │ ├── Offline │ │ │ │ │ │ ├── Offline.js │ │ │ │ │ │ ├── __tests__ │ │ │ │ │ │ │ ├── Offline.test.js │ │ │ │ │ │ │ └── __snapshots__ │ │ │ │ │ │ │ │ └── Offline.test.js.snap │ │ │ │ │ │ └── index.js │ │ │ │ │ └── Online │ │ │ │ │ │ ├── Online.js │ │ │ │ │ │ ├── __tests__ │ │ │ │ │ │ ├── Online.test.js │ │ │ │ │ │ └── __snapshots__ │ │ │ │ │ │ │ └── Online.test.js.snap │ │ │ │ │ │ ├── components │ │ │ │ │ │ ├── LoggedInView │ │ │ │ │ │ │ ├── LoggedInView.js │ │ │ │ │ │ │ ├── __tests__ │ │ │ │ │ │ │ │ ├── LoggedInView.test.js │ │ │ │ │ │ │ │ └── __snapshots__ │ │ │ │ │ │ │ │ │ └── LoggedInView.test.js.snap │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ ├── Add │ │ │ │ │ │ │ │ │ ├── Add.css │ │ │ │ │ │ │ │ │ ├── Add.js │ │ │ │ │ │ │ │ │ ├── __tests__ │ │ │ │ │ │ │ │ │ │ ├── Add.test.js │ │ │ │ │ │ │ │ │ │ └── __snapshots__ │ │ │ │ │ │ │ │ │ │ │ └── Add.test.js.snap │ │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ │ │ ├── Form │ │ │ │ │ │ │ │ │ │ │ ├── Form.css │ │ │ │ │ │ │ │ │ │ │ ├── Form.js │ │ │ │ │ │ │ │ │ │ │ ├── __tests__ │ │ │ │ │ │ │ │ │ │ │ │ ├── Form.test.js │ │ │ │ │ │ │ │ │ │ │ │ └── __snapshots__ │ │ │ │ │ │ │ │ │ │ │ │ │ └── Form.test.js.snap │ │ │ │ │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ │ │ │ │ └── InvalidProtocol │ │ │ │ │ │ │ │ │ │ │ ├── InvalidProtocol.css │ │ │ │ │ │ │ │ │ │ │ ├── InvalidProtocol.js │ │ │ │ │ │ │ │ │ │ │ ├── __tests__ │ │ │ │ │ │ │ │ │ │ │ ├── InvalidProtocol.test.js │ │ │ │ │ │ │ │ │ │ │ └── __snapshots__ │ │ │ │ │ │ │ │ │ │ │ │ └── InvalidProtocol.test.js.snap │ │ │ │ │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ │ │ ├── All │ │ │ │ │ │ │ │ │ ├── All.css │ │ │ │ │ │ │ │ │ ├── All.js │ │ │ │ │ │ │ │ │ ├── __tests__ │ │ │ │ │ │ │ │ │ │ ├── All.test.js │ │ │ │ │ │ │ │ │ │ └── __snapshots__ │ │ │ │ │ │ │ │ │ │ │ └── All.test.js.snap │ │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ │ │ ├── Filters │ │ │ │ │ │ │ │ │ │ │ ├── Filters.css │ │ │ │ │ │ │ │ │ │ │ ├── Filters.js │ │ │ │ │ │ │ │ │ │ │ ├── __tests__ │ │ │ │ │ │ │ │ │ │ │ │ ├── Filters.test.js │ │ │ │ │ │ │ │ │ │ │ │ └── __snapshots__ │ │ │ │ │ │ │ │ │ │ │ │ │ └── Filters.test.js.snap │ │ │ │ │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ │ │ │ │ └── List │ │ │ │ │ │ │ │ │ │ │ ├── List.css │ │ │ │ │ │ │ │ │ │ │ ├── List.js │ │ │ │ │ │ │ │ │ │ │ ├── __tests__ │ │ │ │ │ │ │ │ │ │ │ ├── List.test.js │ │ │ │ │ │ │ │ │ │ │ └── __snapshots__ │ │ │ │ │ │ │ │ │ │ │ │ └── List.test.js.snap │ │ │ │ │ │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ │ │ │ │ └── Article │ │ │ │ │ │ │ │ │ │ │ │ ├── Article.css │ │ │ │ │ │ │ │ │ │ │ │ ├── Article.js │ │ │ │ │ │ │ │ │ │ │ │ ├── __tests__ │ │ │ │ │ │ │ │ │ │ │ │ ├── Article.test.js │ │ │ │ │ │ │ │ │ │ │ │ └── __snapshots__ │ │ │ │ │ │ │ │ │ │ │ │ │ └── Article.test.js.snap │ │ │ │ │ │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ │ │ ├── Error │ │ │ │ │ │ │ │ │ ├── Error.css │ │ │ │ │ │ │ │ │ ├── Error.js │ │ │ │ │ │ │ │ │ ├── __tests__ │ │ │ │ │ │ │ │ │ │ ├── Error.test.js │ │ │ │ │ │ │ │ │ │ └── __snapshots__ │ │ │ │ │ │ │ │ │ │ │ └── Error.test.js.snap │ │ │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ │ │ └── Loading │ │ │ │ │ │ │ │ │ ├── Loading.css │ │ │ │ │ │ │ │ │ ├── Loading.js │ │ │ │ │ │ │ │ │ ├── __tests__ │ │ │ │ │ │ │ │ │ ├── Loading.test.js │ │ │ │ │ │ │ │ │ └── __snapshots__ │ │ │ │ │ │ │ │ │ │ └── Loading.test.js.snap │ │ │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ └── LoggedOutView │ │ │ │ │ │ │ ├── LoggedOutView.js │ │ │ │ │ │ │ ├── __tests__ │ │ │ │ │ │ │ ├── LoggedOutView.test.js │ │ │ │ │ │ │ └── __snapshots__ │ │ │ │ │ │ │ │ └── LoggedOutView.test.js.snap │ │ │ │ │ │ │ └── index.js │ │ │ │ │ │ └── index.js │ │ │ │ └── index.js │ │ │ └── Nav │ │ │ │ ├── Nav.css │ │ │ │ ├── Nav.js │ │ │ │ ├── __tests__ │ │ │ │ ├── Nav.test.js │ │ │ │ └── __snapshots__ │ │ │ │ │ └── Nav.test.js.snap │ │ │ │ └── index.js │ │ │ └── index.js │ ├── index.html │ ├── index.js │ └── redux │ │ ├── actions │ │ ├── error.js │ │ ├── loading.js │ │ ├── online.js │ │ ├── posts.js │ │ ├── user.js │ │ └── view.js │ │ └── reducers │ │ ├── error.js │ │ ├── index.js │ │ ├── loading.js │ │ ├── online.js │ │ ├── posts.js │ │ ├── user.js │ │ └── view.js └── theme │ ├── Button │ ├── Button.css │ ├── Button.js │ └── index.js │ ├── Checkbox │ ├── Checkbox.css │ ├── Checkbox.js │ └── index.js │ ├── Error │ ├── Error.css │ ├── Error.js │ └── index.js │ ├── Input │ ├── Input.css │ ├── Input.js │ └── index.js │ ├── Link │ ├── Link.css │ ├── Link.js │ └── index.js │ ├── Paragraph │ ├── Paragraph.css │ ├── Paragraph.js │ └── index.js │ ├── Quote │ ├── Quote.css │ ├── Quote.js │ └── index.js │ ├── Select │ ├── Select.css │ ├── Select.js │ └── index.js │ ├── Title │ ├── Title.css │ ├── Title.js │ └── index.js │ ├── index.css │ └── index.js ├── webpack.config.js └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | "@babel/preset-react" 5 | ], 6 | "plugins": [ 7 | "transform-class-properties" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parserOptions: { 3 | ecmaVersion: 2018, 4 | sourceType: "module", 5 | ecmaFeatures: { 6 | jsx: true, 7 | impliedStrict: true 8 | } 9 | }, 10 | parser: "babel-eslint", 11 | env: { 12 | browser: true, 13 | node: true, 14 | commonjs: true, 15 | es6: true, 16 | worker: true, 17 | jest: true, 18 | jquery: true, 19 | mongo: true, 20 | applescript: true, 21 | serviceworker: true 22 | }, 23 | plugins: ["prettier", "react"], 24 | extends: ["plugin:prettier/recommended"], 25 | rules: { 26 | "no-console": 1 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | .DS_Store 4 | .vscode 5 | pinbuddy*.zip 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # PinBuddy 4 | 5 | [PinBuddy is a Google Chrome toolbar extension](https://chrome.google.com/webstore/detail/pinbuddy/ppokjacfheflhaojmndcblibahmopkfl) for the [Pinboard](http://pinboard.in) bookmarking service that lets you browse and add new bookmarks with ease. It is fully keyboard accessible and highlights previously bookmarked websites. It uses API token to make a servers calls and never asks you for your password. 6 | 7 | ## Keyboard shortcuts 8 | 9 | - `⌥ + p` (macOS) / `Alt + p` (Windows) - show / hide PinBuddy 10 | - `⌥ + 1` (macOS) / `Alt + 1` (Windows) - go to all view 11 | - `⌥ + 2` (macOS) / `Alt + 2` (Windows) - go to add view 12 | - `⌘ + Enter` (macOS) / `Ctrl + Enter` (Windows) - add bookmark (add view) 13 | - `⌘ + Backspace` (macOS) / `Ctrl + Backspace` (Windows) - delete bookmark (all view) 14 | 15 | ## Thanks to, thanks for… 16 | 17 | - Thanks to [Miłosz Kaniuk](https://www.behance.net/miloszkanibf79) for great logo and UI / UX recommendations 18 | - Thanks to [Gregory Assasie](https://twitter.com/gregory_jarvez) for dev tips 19 | - Thanks to [Zuzanna Rupińska](https://www.instagram.com/zuzanna.rupinska/) for brewing a coffee for me during development :-\* 20 | - Thanks to [Lionel Foucambert](https://github.com/LionelFW) for the French translation 21 | - Thanks to [Christian](https://github.com/chmartinez) for the Spanish translation 22 | - Thanks to [Fredrika](https://github.com/femtioelva) for the Swedish translation 23 | - Thanks to [David Hölkeskamp](https://github.com/dhkamp) for the German translation 24 | - Thanks to [Bublik](https://github.com/Bigbublik) for the Russian translation 25 | - Thanks to [Frode Grimstad Bang](https://www.frodebang.com/) for the Norwegian translation 26 | - Thanks to [Kostas Liberopoulos](https://github.com/KostasLib) for the Greek translation 27 | - Thanks to [Igor Guastalla](https://github.com/guastallaigor) for the Brazilian translation 28 | - Thanks to [Oskari Holopainen](https://github.com/Ikaros1510) for the Finnish translation 29 | - Thanks to [Cristian Bell](https://github.com/cristianbell) for the Romanian translation 30 | - Thanks to [Xavier Marquès](https://github.com/wolframtheta) and [oriolhub](https://github.com/oriolhub) for the Catalan translation 31 | - Thanks to [Tom Veldman](https://github.com/progBorg) for the Dutch translation 32 | - Thanks to [Ana Gilda Rodrigues](https://github.com/AnaGilda) for the Portuguese translation 33 | - Thanks to [Nikita Rudenko](https://github.com/nick-rudenko) for the Ukrainian translation 34 | - Thanks to [Yotam Salmon](https://github.com/yotam180) for the Hebrew translation 35 | - Thanks to [murat emir cabaroğlu](https://github.com/mrTmr12) for the Turkish translation 36 | - Thanks to [Jovan Ferryal E. F.](https://wecreative.co.id/) for the Indonesian translation 37 | - Thanks to [Aleš Jiránek](https://github.com/AlesJiranek) for the Czech translation 38 | - Thanks to [Po Chun, Lu](https://github.com/Sirius207) for the traditional Chinese translation 39 | - Thanks to [giovannipessiva](https://github.com/giovannipessiva) for the Italian translation 40 | 41 | - Potentially you… 42 | -------------------------------------------------------------------------------- /config/jest-style-mock.js: -------------------------------------------------------------------------------- 1 | export default {}; 2 | -------------------------------------------------------------------------------- /config/jest.js: -------------------------------------------------------------------------------- 1 | import Enzyme, { mount } from "enzyme"; 2 | import Adapter from "enzyme-adapter-react-16"; 3 | 4 | // adapter config 5 | Enzyme.configure({ adapter: new Adapter() }); 6 | 7 | // mock global chrome 8 | global.chrome = { 9 | i18n: { 10 | getMessage: jest.fn(translationKey => translationKey) 11 | }, 12 | tabs: { 13 | query: jest.fn() 14 | } 15 | }; 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pinboard-x", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "license": "MIT", 6 | "scripts": { 7 | "dev": "webpack --watch --mode=development", 8 | "build": "webpack --mode=production && zip -r \"$(date +\"pinbuddy_%Y_%m_%d_%I_%M_%S\").zip\" dist", 9 | "test": "jest", 10 | "test:watch": "jest --watch" 11 | }, 12 | "jest": { 13 | "setupTestFrameworkScriptFile": "/config/jest.js", 14 | "moduleNameMapper": { 15 | "^theme(.*)$": "/src/theme$1", 16 | "^redux-popup(.*)$": "/src/popup/redux$1", 17 | "^redux-options(.*)$": "/src/options/redux$1", 18 | "\\.css$": "/config/jest-style-mock.js" 19 | } 20 | }, 21 | "devDependencies": { 22 | "@babel/core": "^7.1.2", 23 | "@babel/preset-env": "^7.0.0", 24 | "@babel/preset-react": "^7.0.0", 25 | "babel-core": "7.0.0-bridge.0", 26 | "babel-eslint": "^10.0.1", 27 | "babel-jest": "^23.6.0", 28 | "babel-loader": "^8.0.4", 29 | "babel-plugin-transform-class-properties": "^6.24.1", 30 | "clean-webpack-plugin": "^0.1.19", 31 | "copy-webpack-plugin": "^4.5.3", 32 | "css-loader": "^1.0.0", 33 | "enzyme": "^3.7.0", 34 | "enzyme-adapter-react-16": "^1.6.0", 35 | "eslint": "^5.7.0", 36 | "eslint-config-prettier": "^3.1.0", 37 | "eslint-loader": "^2.1.1", 38 | "eslint-plugin-prettier": "^3.0.0", 39 | "eslint-plugin-react": "^7.11.1", 40 | "html-webpack-plugin": "^3.2.0", 41 | "jest": "^23.6.0", 42 | "prettier": "^1.14.3", 43 | "style-loader": "^0.23.1", 44 | "webpack": "^4.21.0", 45 | "webpack-cli": "^3.1.2" 46 | }, 47 | "dependencies": { 48 | "prop-types": "^15.6.2", 49 | "react": "^16.5.0", 50 | "react-dom": "^16.5.0", 51 | "react-redux": "^5.0.7", 52 | "redux": "^4.0.1", 53 | "redux-thunk": "^2.3.0" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/_locales/en/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "PinBuddy" 4 | }, 5 | "extensionDescription": { 6 | "message": "Browse your Pinboard bookmarks and add new ones with ease." 7 | }, 8 | 9 | "optionsTitle": { 10 | "message": "PinBuddy" 11 | }, 12 | "optionsFooter": { 13 | "message": "Copyright © 2018 Pawel Grzybek. Issues & feature requests: Twitter, GitHub." 14 | }, 15 | "optionsLoginManual": { 16 | "message": "An authentication token is a short opaque identifier in the form \"username:TOKEN\" that allows PinBuddy to make API calls without ever asking for your password. You can find your API token on a settings page under the password tab." 17 | }, 18 | "optionsLoginError": { 19 | "message": "Error. Please make sure that you use a correct token and that your internet connection is stable." 20 | }, 21 | "optionsLoginFormLabel": { 22 | "message": "API Token" 23 | }, 24 | "optionsLoginFormPlaceholder": { 25 | "message": "username:TOKEN" 26 | }, 27 | "optionsLoginFormSubmit": { 28 | "message": "Submit" 29 | }, 30 | "optionsOfflineMessageOne": { 31 | "message": "Look at the official Pinboard tour." 32 | }, 33 | "optionsOfflineMessageQuote": { 34 | "message": "Pinboard is a personal archive for things you find online and don't want to forget." 35 | }, 36 | "optionsOfflineMessageTwo": { 37 | "message": "Exactly — \"online\". Please make sure you have a stable internet connection." 38 | }, 39 | "optionsWelcomeMessage": { 40 | "message": "Hi. You are logged in as $name$.", 41 | "placeholders": { 42 | "name": { 43 | "content": "$1", 44 | "example": "Pawel" 45 | } 46 | } 47 | }, 48 | "optionsLogOut": { 49 | "message": "Log out" 50 | }, 51 | "optionsDefaultView": { 52 | "message": "Default view" 53 | }, 54 | "optionsDefaultViewAllText": { 55 | "message": "all" 56 | }, 57 | "optionsDefaultViewAddText": { 58 | "message": "add url" 59 | }, 60 | "optionsPrivateCheckboxByDefault": { 61 | "message": "\"Private\" checkbox by default (add url view)" 62 | }, 63 | "optionsToReadChecboxByDefault": { 64 | "message": "\"To read\" checkbox by default (add url view)" 65 | }, 66 | "optionsEnableSystemNotifications": { 67 | "message": "Enable system notifications" 68 | }, 69 | "optionsShortcutDescriptionToggle": { 70 | "message": "show / hide PinBuddy" 71 | }, 72 | "optionsShortcutDescriptionGoToAll": { 73 | "message": "go to all view" 74 | }, 75 | "optionsShortcutDescriptionGoToAdd": { 76 | "message": "go to add view" 77 | }, 78 | "optionsShortcutDescriptionAddBookmark": { 79 | "message": "add bookmark (add view)" 80 | }, 81 | "optionsShortcutDescriptionOpenInBackground": { 82 | "message": "open bookmark in background (all view)" 83 | }, 84 | "optionsShortcutDescriptionDeleteBookmark": { 85 | "message": "delete bookmark (all view)" 86 | }, 87 | 88 | "popupOpenYourPinboardProfile": { 89 | "message": "Open your Pinboard Profile" 90 | }, 91 | "popupBrowseAllTitle": { 92 | "message": "Browse all" 93 | }, 94 | "popupBrowseAllText": { 95 | "message": "all" 96 | }, 97 | "popupAddURLTitle": { 98 | "message": "Add URL" 99 | }, 100 | "popupAddURLText": { 101 | "message": "add url" 102 | }, 103 | "popupErrorMessage": { 104 | "message": "Error occurred. Please make sure that your token is valid, and that your internet connection and the Pinboard API status is stable." 105 | }, 106 | "popupErrorButtonMessage": { 107 | "message": "Go to option page" 108 | }, 109 | "popupFiltersSearchLabel": { 110 | "message": "Search" 111 | }, 112 | "popupFiltersSearchPlaceholder": { 113 | "message": "Search term" 114 | }, 115 | "popupFiltersFilterButton": { 116 | "message": "Filters" 117 | }, 118 | "popupFiltersCheckboxPrivate": { 119 | "message": "Private" 120 | }, 121 | "popupFiltersCheckboxPublic": { 122 | "message": "Public" 123 | }, 124 | "popupFiltersCheckboxUnread": { 125 | "message": "Unread" 126 | }, 127 | "popupFiltersCheckboxUntagged": { 128 | "message": "Untagged" 129 | }, 130 | "popupArticleButtonCancel": { 131 | "message": "cancel" 132 | }, 133 | "popupArticleButtonDelete": { 134 | "message": "delete" 135 | }, 136 | "popupAddTitleLabel": { 137 | "message": "Title" 138 | }, 139 | "popupAddTitleInvalidMessage": { 140 | "message": "Max 255 characters" 141 | }, 142 | "popupAddDescriptionLabel": { 143 | "message": "Description" 144 | }, 145 | "popupAddDescriptionInvalidMessage": { 146 | "message": "Max 65536 characters" 147 | }, 148 | "popupAddTagsLabel": { 149 | "message": "Tags (space separated)" 150 | }, 151 | "popupAddTagsInvalidMessage": { 152 | "message": "Max 255 characters" 153 | }, 154 | "popupAddPrivateCheckboxLabel": { 155 | "message": "private" 156 | }, 157 | "popupAddReadLaterCheckboxLabel": { 158 | "message": "read later" 159 | }, 160 | "popupAddBookmarkButton": { 161 | "message": "add bookmark" 162 | }, 163 | "popupReAddBookmarkButton": { 164 | "message": "override bookmark" 165 | }, 166 | "popupLoggedOutViewMessage": { 167 | "message": "An authentication token is a short opaque identifier in the form \"username:TOKEN\" that allows PinBuddy to make API calls without ever asking for your password. You can find your API token on a settings page under the password tab." 168 | }, 169 | "popupLoggedOutViewButtonMessage": { 170 | "message": "Go to option page to log in" 171 | }, 172 | "popupOfflineMessageOne": { 173 | "message": "Look at the official Pinboard tour." 174 | }, 175 | "popupOfflineMessageQuote": { 176 | "message": "Pinboard is a personal archive for things you find online and don't want to forget." 177 | }, 178 | "popupOfflineMessageTwo": { 179 | "message": "Exactly — \"online\". Make sure you have a stable internet connection please." 180 | }, 181 | "popupInvalidProtocolParagraph": { 182 | "message": "Allowed protocols are http, https, javascript, mailto, ftp and file. Unfortunately you cannot save this URL." 183 | }, 184 | "popupInvalidProtocolButton": { 185 | "message": "Find out more in Pinboard API documentation" 186 | }, 187 | 188 | "notificationSaveSucessful": { 189 | "message": "URL saved successfully" 190 | }, 191 | "notificationDeleteSucessful": { 192 | "message": "URL deleted successfully" 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/_locales/he/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "PinBuddy" 4 | }, 5 | "extensionDescription": { 6 | "message": "נהל את כרטיסיות Pinboard שלך והוסף חדשות בקלות" 7 | }, 8 | 9 | "optionsTitle": { 10 | "message": "PinBuddy" 11 | }, 12 | "optionsFooter": { 13 | "message": "כל הזכויות שמורות © 2018 Pawel Grzybek. לבעיות, מענות ובקשות: טוויטר, גיטהאב." 14 | }, 15 | "optionsLoginManual": { 16 | "message": "מפתח התחברות הוא מזהה קצר מהצורה \"username:TOKEN\" המאפשר ל-PinBuddy לשלוח בקשות API מבלי לבקש הרשאות כל פעם מחדש. תוכל למצוא את מפתח ההתחברות שלך כאן: דף ההגדרות תחת הכרטיסייה 'סיסמה'." 17 | }, 18 | "optionsLoginError": { 19 | "message": "שגיאה. אנא וודא שאתה משתמש במפתח ההתחברות הנכון ושחיבור האינטרנט שלך יציב." 20 | }, 21 | "optionsLoginFormLabel": { 22 | "message": "מפתח API" 23 | }, 24 | "optionsLoginFormPlaceholder": { 25 | "message": "שם משתמש:מפתח" 26 | }, 27 | "optionsLoginFormSubmit": { 28 | "message": "שלח" 29 | }, 30 | "optionsOfflineMessageOne": { 31 | "message": "גלה עוד בסיור Pinboard הרשמי." 32 | }, 33 | "optionsOfflineMessageQuote": { 34 | "message": "Pinboard הוא ארכיון אישי בעבור דברים שמצאת באינטרנט ולא תרצה לשכוח." 35 | }, 36 | "optionsOfflineMessageTwo": { 37 | "message": "בדיוק — \"מחובר\". אנא בדוק שיש לך חיבור אינטרנט יציב." 38 | }, 39 | "optionsWelcomeMessage": { 40 | "message": "היי. אתה מחובר כ$name$.", 41 | "placeholders": { 42 | "name": { 43 | "content": "$1", 44 | "example": "דני" 45 | } 46 | } 47 | }, 48 | "optionsLogOut": { 49 | "message": "יציאה" 50 | }, 51 | "optionsDefaultView": { 52 | "message": "ברירת מחדל" 53 | }, 54 | "optionsDefaultViewAllText": { 55 | "message": "הכל" 56 | }, 57 | "optionsDefaultViewAddText": { 58 | "message": "הוסף קישור" 59 | }, 60 | "optionsPrivateCheckboxByDefault": { 61 | "message": "סימון \"אישי\" כברירת מחדל (בהוספת קישור)" 62 | }, 63 | "optionsToReadChecboxByDefault": { 64 | "message": "סימון \"לקריאה\" כברירת מחדל (בהוספת קישור)" 65 | }, 66 | "optionsEnableSystemNotifications": { 67 | "message": "הפעל התראות מערכת" 68 | }, 69 | "optionsShortcutDescriptionToggle": { 70 | "message": "הראה או הסתר את PinBuddy" 71 | }, 72 | "optionsShortcutDescriptionGoToAll": { 73 | "message": "לתצוגה הכללית" 74 | }, 75 | "optionsShortcutDescriptionGoToAdd": { 76 | "message": "להוספת תצוגה חדשה" 77 | }, 78 | "optionsShortcutDescriptionAddBookmark": { 79 | "message": "הוסף כרטיסייה (הוסף תצוגה)" 80 | }, 81 | "optionsShortcutDescriptionOpenInBackground": { 82 | "message": "פתח כרטיסייה ברקע (תצוגה כללית)" 83 | }, 84 | "optionsShortcutDescriptionDeleteBookmark": { 85 | "message": "מחק כרטיסייה (תצוגה כללית)" 86 | }, 87 | 88 | "popupOpenYourPinboardProfile": { 89 | "message": "פתח את פרופיל הPinboard שלך" 90 | }, 91 | "popupBrowseAllTitle": { 92 | "message": "ראה הכל" 93 | }, 94 | "popupBrowseAllText": { 95 | "message": "הכל" 96 | }, 97 | "popupAddURLTitle": { 98 | "message": "הוסף קישור" 99 | }, 100 | "popupAddURLText": { 101 | "message": "הוסף קישור" 102 | }, 103 | "popupErrorMessage": { 104 | "message": "שגיאה. אנא בדוק שמפתח ההתחברות שלך תקין, ושחיבור האינטרנט שלך וסטטוס הPinboard API שלך יציבים." 105 | }, 106 | "popupErrorButtonMessage": { 107 | "message": "לדף האפשרויות" 108 | }, 109 | "popupFiltersSearchLabel": { 110 | "message": "חיפוש" 111 | }, 112 | "popupFiltersSearchPlaceholder": { 113 | "message": "חיפוש מושג" 114 | }, 115 | "popupFiltersFilterButton": { 116 | "message": "פילטרים" 117 | }, 118 | "popupFiltersCheckboxPrivate": { 119 | "message": "פרטי" 120 | }, 121 | "popupFiltersCheckboxPublic": { 122 | "message": "ציבורי" 123 | }, 124 | "popupFiltersCheckboxUnread": { 125 | "message": "לא נקרא" 126 | }, 127 | "popupFiltersCheckboxUntagged": { 128 | "message": "ללא תג" 129 | }, 130 | "popupArticleButtonCancel": { 131 | "message": "ביטול" 132 | }, 133 | "popupArticleButtonDelete": { 134 | "message": "מחיקה" 135 | }, 136 | "popupAddTitleLabel": { 137 | "message": "כותרת" 138 | }, 139 | "popupAddTitleInvalidMessage": { 140 | "message": "לכל היותר 255 תווים" 141 | }, 142 | "popupAddDescriptionLabel": { 143 | "message": "תיאור" 144 | }, 145 | "popupAddDescriptionInvalidMessage": { 146 | "message": "לכל היותר 65536 תווים" 147 | }, 148 | "popupAddTagsLabel": { 149 | "message": "תגים (הפרד באמצעות רווח)" 150 | }, 151 | "popupAddTagsInvalidMessage": { 152 | "message": "לכל היותר 255 תווים" 153 | }, 154 | "popupAddPrivateCheckboxLabel": { 155 | "message": "פרטי" 156 | }, 157 | "popupAddReadLaterCheckboxLabel": { 158 | "message": "קרא מאוחר יותר" 159 | }, 160 | "popupAddBookmarkButton": { 161 | "message": "הוסף כרטיסייה" 162 | }, 163 | "popupReAddBookmarkButton": { 164 | "message": "דריסת כרטיסייה" 165 | }, 166 | "popupLoggedOutViewMessage": { 167 | "message": "מפתח התחברות הוא מזהה קצר מהצורה \"username:TOKEN\" המאפשר ל-PinBuddy לשלוח בקשות API מבלי לבקש הרשאות כל פעם מחדש. תוכל למצוא את מפתח ההתחברות שלך כאן: דף ההגדרות תחת הכרטיסייה 'סיסמה'." 168 | }, 169 | "popupLoggedOutViewButtonMessage": { 170 | "message": "עבור לדף האפשרויות להתחברות" 171 | }, 172 | "popupOfflineMessageOne": { 173 | "message": "גלה את סיור Pinboard הרשמי." 174 | }, 175 | "popupOfflineMessageQuote": { 176 | "message": "Pinboard הוא ארכיון אישי בעבור דברים שמצאת באינטרנט ולא תרצה לשכוח." 177 | }, 178 | "popupOfflineMessageTwo": { 179 | "message": "בדיוק — \"מחובר\". אנא בדוק שחיבור האינטרנט שלך יציב." 180 | }, 181 | "popupInvalidProtocolParagraph": { 182 | "message": "פרוטוקולים מורשים: http, https, javascript, mailto, ftp and file. לצערנו, לא תוכל לשמור את הקישור הזה." 183 | }, 184 | "popupInvalidProtocolButton": { 185 | "message": "קרא עוד במסמכים של Pinboard API" 186 | }, 187 | 188 | "notificationSaveSucessful": { 189 | "message": "הקישור נשמר בהצלחה" 190 | }, 191 | "notificationDeleteSucessful": { 192 | "message": "הקישור נמחק בהצלחה" 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/_locales/locales-to-support.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ar - Arabic 4 | am - Amharic 5 | bg - Bulgarian 6 | bn - Bengali 7 | ca - Catalan 8 | cs - Czech 9 | da - Danish 10 | de - German 11 | el - Greek 12 | en - English 13 | en_GB - English (Great Britain) 14 | en_US - English (USA) 15 | es - Spanish 16 | es_419 - Spanish (Latin America and Caribbean) 17 | et - Estonian 18 | fa - Persian 19 | fi - Finnish 20 | fil - Filipino 21 | fr - French 22 | gu - Gujarati 23 | he - Hebrew 24 | hi - Hindi 25 | hr - Croatian 26 | hu - Hungarian 27 | id - Indonesian 28 | it - Italian 29 | ja - Japanese 30 | kn - Kannada 31 | ko - Korean 32 | lt - Lithuanian 33 | lv - Latvian 34 | ml - Malayalam 35 | mr - Marathi 36 | ms - Malay 37 | nl - Dutch 38 | no - Norwegian 39 | pl - Polish 40 | pt_BR - Portuguese (Brazil) 41 | pt_PT - Portuguese (Portugal) 42 | ro - Romanian 43 | ru - Russian 44 | sk - Slovak 45 | sl - Slovenian 46 | sr - Serbian 47 | sv - Swedish 48 | sw - Swahili 49 | ta - Tamil 50 | te - Telugu 51 | th - Thai 52 | tr - Turkish 53 | uk - Ukrainian 54 | vi - Vietnamese 55 | zh_CN - Chinese (China) 56 | zh_TW - Chinese (Taiwan) 57 | -------------------------------------------------------------------------------- /src/_locales/sv/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "PinBuddy" 4 | }, 5 | "extensionDescription": { 6 | "message": "Sök bland dina Pinboardbokmärken och lägg enkelt till nya." 7 | }, 8 | 9 | "optionsTitle": { 10 | "message": "PinBuddy" 11 | }, 12 | "optionsFooter": { 13 | "message": "Copyright © 2018 Pawel Grzybek. Problem & features: Twitter, GitHub." 14 | }, 15 | "optionsLoginManual": { 16 | "message": "En autentiseringstoken används för att identifiera genom formatet \"användarnamn:TOKEN\" och ger PinBuddy tillåtelse att göra API-anrop utan att fråga om ditt lösenord. Du kan hitta din API-token i inställningar, under lösenordsfliken." 17 | }, 18 | "optionsLoginError": { 19 | "message": "Fel har inträffat. Vänligen kontrollera att du använder rätt token och är ansluten till internet." 20 | }, 21 | "optionsLoginFormLabel": { 22 | "message": "API-token" 23 | }, 24 | "optionsLoginFormPlaceholder": { 25 | "message": "användarnamn:TOKEN" 26 | }, 27 | "optionsLoginFormSubmit": { 28 | "message": "Skicka" 29 | }, 30 | "optionsOfflineMessageOne": { 31 | "message": "Besök den officiella Pinboard-guiden." 32 | }, 33 | "optionsOfflineMessageQuote": { 34 | "message": "Pinboard är ett personligt arkiv för det du hittar online och inte vill glömma." 35 | }, 36 | "optionsOfflineMessageTwo": { 37 | "message": "Just det — \"online\". Vänligen kontrollera att du är ansluten till internet." 38 | }, 39 | "optionsWelcomeMessage": { 40 | "message": "Hej! Du är inloggad som $name$.", 41 | "placeholders": { 42 | "name": { 43 | "content": "$1", 44 | "example": "Pawel" 45 | } 46 | } 47 | }, 48 | "optionsLogOut": { 49 | "message": "Logga ut" 50 | }, 51 | "optionsDefaultView": { 52 | "message": "Standardvy" 53 | }, 54 | "optionsDefaultViewAllText": { 55 | "message": "alla" 56 | }, 57 | "optionsDefaultViewAddText": { 58 | "message": "lägg till sida" 59 | }, 60 | "optionsPrivateCheckboxByDefault": { 61 | "message": "\"Privat\" checkbox som standard (i lägg till sida)" 62 | }, 63 | "optionsToReadChecboxByDefault": { 64 | "message": "\"To read\" checkbox som standard (i lägg till sida)" 65 | }, 66 | "optionsEnableSystemNotifications": { 67 | "message": "Tillåt systemnotifikationer" 68 | }, 69 | "optionsShortcutDescriptionToggle": { 70 | "message": "visa / dölj PinBuddy" 71 | }, 72 | "optionsShortcutDescriptionGoToAll": { 73 | "message": "gå till alla-vyn" 74 | }, 75 | "optionsShortcutDescriptionGoToAdd": { 76 | "message": "gå till lägg till vy" 77 | }, 78 | "optionsShortcutDescriptionAddBookmark": { 79 | "message": "lägg till bokmärke (lägg till vy)" 80 | }, 81 | "optionsShortcutDescriptionOpenInBackground": { 82 | "message": "visa bokmärke i bakgrunden (alla vyer)" 83 | }, 84 | "optionsShortcutDescriptionDeleteBookmark": { 85 | "message": "Ta bort bokmärke (alla vyer)" 86 | }, 87 | 88 | "popupOpenYourPinboardProfile": { 89 | "message": "Visa din Pinboardprofil" 90 | }, 91 | "popupBrowseAllTitle": { 92 | "message": "Visa alla" 93 | }, 94 | "popupBrowseAllText": { 95 | "message": "alla" 96 | }, 97 | "popupAddURLTitle": { 98 | "message": "Lägg till sida" 99 | }, 100 | "popupAddURLText": { 101 | "message": "lägg till sida" 102 | }, 103 | "popupErrorMessage": { 104 | "message": "Ett fel har inträffat. Vänligen kontrollera att din token är giltig, att du är ansluten till internet samt att Pinboards API status är stabil." 105 | }, 106 | "popupErrorButtonMessage": { 107 | "message": "Gå till inställningar" 108 | }, 109 | "popupFiltersSearchLabel": { 110 | "message": "Sök" 111 | }, 112 | "popupFiltersSearchPlaceholder": { 113 | "message": "Sökterm" 114 | }, 115 | "popupFiltersFilterButton": { 116 | "message": "Filter" 117 | }, 118 | "popupFiltersCheckboxPrivate": { 119 | "message": "Privat" 120 | }, 121 | "popupFiltersCheckboxPublic": { 122 | "message": "Publik" 123 | }, 124 | "popupFiltersCheckboxUnread": { 125 | "message": "Oläst" 126 | }, 127 | "popupFiltersCheckboxUntagged": { 128 | "message": "Otaggat" 129 | }, 130 | "popupArticleButtonCancel": { 131 | "message": "avbryt" 132 | }, 133 | "popupArticleButtonDelete": { 134 | "message": "radera" 135 | }, 136 | "popupAddTitleLabel": { 137 | "message": "Titel" 138 | }, 139 | "popupAddTitleInvalidMessage": { 140 | "message": "Max 255 tecken" 141 | }, 142 | "popupAddDescriptionLabel": { 143 | "message": "Beskrivning" 144 | }, 145 | "popupAddDescriptionInvalidMessage": { 146 | "message": "Max 65536 tecken" 147 | }, 148 | "popupAddTagsLabel": { 149 | "message": "Taggar (separeras med mellanslag)" 150 | }, 151 | "popupAddTagsInvalidMessage": { 152 | "message": "Max 255 tecken" 153 | }, 154 | "popupAddPrivateCheckboxLabel": { 155 | "message": "privat" 156 | }, 157 | "popupAddReadLaterCheckboxLabel": { 158 | "message": "Läs senare" 159 | }, 160 | "popupAddBookmarkButton": { 161 | "message": "lägg till bokmärke" 162 | }, 163 | "popupReAddBookmarkButton": { 164 | "message": "ersätt bokmärke" 165 | }, 166 | "popupLoggedOutViewMessage": { 167 | "message": "En autentiseringstoken används för att identifiera genom formatet \"användarnamn:TOKEN\" och tillåter PinBuddy att göra API-anrop utan att fråga om ditt lösenord. Du kan hitta din API-token i inställningar, under lösenordsfliken." 168 | }, 169 | "popupLoggedOutViewButtonMessage": { 170 | "message": "Gå till inställningar för att logga in" 171 | }, 172 | "popupOfflineMessageOne": { 173 | "message": "Besök den officiella Pinboard-guiden." 174 | }, 175 | "popupOfflineMessageQuote": { 176 | "message": "Pinboard är ett personligt arkiv för det du hittar online och inte vill glömma." 177 | }, 178 | "popupOfflineMessageTwo": { 179 | "message": "Just det — \"online\". Vänligen kontrollera att du är ansluten till internet." 180 | }, 181 | "popupInvalidProtocolParagraph": { 182 | "message": "Tillåtna protokoll är http, https, javascript, mailto, ftp och file. Det gick tyvärr inte att spara denna sidan." 183 | }, 184 | "popupInvalidProtocolButton": { 185 | "message": "Läs mer i Pinboards API-dokumentation" 186 | }, 187 | 188 | "notificationSaveSucessful": { 189 | "message": "Sidan har sparats" 190 | }, 191 | "notificationDeleteSucessful": { 192 | "message": "Sidan har raderats" 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/_locales/zh-TW/messages.json: -------------------------------------------------------------------------------- 1 | { 2 | "extensionName": { 3 | "message": "PinBuddy" 4 | }, 5 | "extensionDescription": { 6 | "message": "透過 Pinboard 輕鬆瀏覽並新增書籤。" 7 | }, 8 | 9 | "optionsTitle": { 10 | "message": "PinBuddy" 11 | }, 12 | "optionsFooter": { 13 | "message": "Copyright © 2018 Pawel Grzybek 問題 & 功能 請求: Twitter, GitHub." 14 | }, 15 | "optionsLoginManual": { 16 | "message": "認證 token 是個簡短的識別碼,格式為\"username:TOKEN\" 能讓 PinBuddy 發送 API 請求而不需要向您詢問您的密碼。 您可以在 密碼分頁下方的設定頁面 找到您的 API Token。" 17 | }, 18 | "optionsLoginError": { 19 | "message": "發生錯誤,請確認您使用正確的 token 並且有穩定的網路連線。" 20 | }, 21 | "optionsLoginFormLabel": { 22 | "message": "API Token" 23 | }, 24 | "optionsLoginFormPlaceholder": { 25 | "message": "username:TOKEN" 26 | }, 27 | "optionsLoginFormSubmit": { 28 | "message": "送出" 29 | }, 30 | "optionsOfflineMessageOne": { 31 | "message": "查看官方Pinboard 指南." 32 | }, 33 | "optionsOfflineMessageQuote": { 34 | "message": "Pinboard 是個人化的封存工具,您可以用來保存您在網路上找到且不想遺忘的資訊" 35 | }, 36 | "optionsOfflineMessageTwo": { 37 | "message": "確實 — \"連線\". 請確認您有穩定的網路連線" 38 | }, 39 | "optionsWelcomeMessage": { 40 | "message": "嗨. 您目前以 $name$ 登入", 41 | "placeholders": { 42 | "name": { 43 | "content": "$1", 44 | "example": "Pawel" 45 | } 46 | } 47 | }, 48 | "optionsLogOut": { 49 | "message": "登出" 50 | }, 51 | "optionsDefaultView": { 52 | "message": "預設外觀" 53 | }, 54 | "optionsDefaultViewAllText": { 55 | "message": "全部" 56 | }, 57 | "optionsDefaultViewAddText": { 58 | "message": "新增 url" 59 | }, 60 | "optionsPrivateCheckboxByDefault": { 61 | "message": "\"非公開\" 預設 checkbox (新增 url view)" 62 | }, 63 | "optionsToReadChecboxByDefault": { 64 | "message": "\"準備閱讀\" 預設 checkbox (新增 url view)" 65 | }, 66 | "optionsEnableSystemNotifications": { 67 | "message": "啟用系統通知" 68 | }, 69 | "optionsShortcutDescriptionToggle": { 70 | "message": "顯示 / 隱藏 PinBuddy" 71 | }, 72 | "optionsShortcutDescriptionGoToAll": { 73 | "message": "前往全部頁面" 74 | }, 75 | "optionsShortcutDescriptionGoToAdd": { 76 | "message": "前往新增頁面" 77 | }, 78 | "optionsShortcutDescriptionAddBookmark": { 79 | "message": "新增書籤" 80 | }, 81 | "optionsShortcutDescriptionOpenInBackground": { 82 | "message": "在背景開啟書籤" 83 | }, 84 | "optionsShortcutDescriptionDeleteBookmark": { 85 | "message": "刪除書籤" 86 | }, 87 | 88 | "popupOpenYourPinboardProfile": { 89 | "message": "開啟您的 Pinboard 檔案" 90 | }, 91 | "popupBrowseAllTitle": { 92 | "message": "瀏覽全部" 93 | }, 94 | "popupBrowseAllText": { 95 | "message": "全部" 96 | }, 97 | "popupAddURLTitle": { 98 | "message": "新增 URL 標題" 99 | }, 100 | "popupAddURLText": { 101 | "message": "新增 url 文字" 102 | }, 103 | "popupErrorMessage": { 104 | "message": "發生錯誤. 請確認您的 token 是有效的,且網路連接與Pinboard API 狀態是穩定的" 105 | }, 106 | "popupErrorButtonMessage": { 107 | "message": "進入設定頁面" 108 | }, 109 | "popupFiltersSearchLabel": { 110 | "message": "搜尋" 111 | }, 112 | "popupFiltersSearchPlaceholder": { 113 | "message": "搜尋關鍵字" 114 | }, 115 | "popupFiltersFilterButton": { 116 | "message": "過濾" 117 | }, 118 | "popupFiltersCheckboxPrivate": { 119 | "message": "不公開" 120 | }, 121 | "popupFiltersCheckboxPublic": { 122 | "message": "公開" 123 | }, 124 | "popupFiltersCheckboxUnread": { 125 | "message": "未讀" 126 | }, 127 | "popupFiltersCheckboxUntagged": { 128 | "message": "未標籤" 129 | }, 130 | "popupArticleButtonCancel": { 131 | "message": "取消" 132 | }, 133 | "popupArticleButtonDelete": { 134 | "message": "刪除" 135 | }, 136 | "popupAddTitleLabel": { 137 | "message": "標題" 138 | }, 139 | "popupAddTitleInvalidMessage": { 140 | "message": "最多 255 字元" 141 | }, 142 | "popupAddDescriptionLabel": { 143 | "message": "說明" 144 | }, 145 | "popupAddDescriptionInvalidMessage": { 146 | "message": "最多 65536 字元" 147 | }, 148 | "popupAddTagsLabel": { 149 | "message": "標籤 (以空白分隔)" 150 | }, 151 | "popupAddTagsInvalidMessage": { 152 | "message": "最多 255 字元" 153 | }, 154 | "popupAddPrivateCheckboxLabel": { 155 | "message": "不公開" 156 | }, 157 | "popupAddReadLaterCheckboxLabel": { 158 | "message": "稍後閱讀" 159 | }, 160 | "popupAddBookmarkButton": { 161 | "message": "新增書籤" 162 | }, 163 | "popupReAddBookmarkButton": { 164 | "message": "覆蓋書籤" 165 | }, 166 | "popupLoggedOutViewMessage": { 167 | "message": "認證 token 是個簡短的識別碼,格式為\"username:TOKEN\" 能讓 PinBuddy 發送 API 請求而不需要向您詢問您的密碼。 您可以在 密碼分頁下方的設定頁面 找到您的 API Token。" 168 | }, 169 | "popupLoggedOutViewButtonMessage": { 170 | "message": "前往設定頁面來登入" 171 | }, 172 | "popupOfflineMessageOne": { 173 | "message": "查看官方 Pinboard 指南." 174 | }, 175 | "popupOfflineMessageQuote": { 176 | "message": "Pinboard 是個人化的封存工具,您可以用來保存您在網路上找到的不想遺忘的資訊" 177 | }, 178 | "popupOfflineMessageTwo": { 179 | "message": "確實 — \"連線\". 請確認您有穩定的網路連線" 180 | }, 181 | "popupInvalidProtocolParagraph": { 182 | "message": "允許使用的協定有http, https, javascript, mailto, ftp and file. 抱歉您無法儲存此 URL." 183 | }, 184 | "popupInvalidProtocolButton": { 185 | "message": "在 Pinboard API 文件中查看更多" 186 | }, 187 | 188 | "notificationSaveSucessful": { 189 | "message": "URL 成功儲存" 190 | }, 191 | "notificationDeleteSucessful": { 192 | "message": "URL 成功刪除" 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/background/index.js: -------------------------------------------------------------------------------- 1 | // icon change when url exists 2 | const listenForIconChange = tab => { 3 | const { url, active } = tab; 4 | if (url && active) { 5 | chrome.storage.local.get(["posts"], posts => { 6 | if (posts.posts) { 7 | const postExists = !!posts.posts.find(post => post.href === url); 8 | chrome.browserAction.setIcon({ 9 | path: postExists 10 | ? "/icons/icon-active-128.png" 11 | : "/icons/icon-128.png" 12 | // path: { 13 | // 16: postExists ? '/icons/icon-active-16.png' : '/icons/icon-16.png', 14 | // 48: postExists ? '/icons/icon-active-48.png' : '/icons/icon-48.png', 15 | // 128: postExists ? '/icons/icon-active-128.png' : '/icons/icon-128.png' 16 | // } 17 | }); 18 | } 19 | }); 20 | } 21 | }; 22 | 23 | // listen to url updates 24 | chrome.tabs.onUpdated.addListener(result => { 25 | if (result) { 26 | chrome.tabs.get(result, listenForIconChange); 27 | } 28 | }); 29 | 30 | // listen to new tabs 31 | chrome.tabs.onActivated.addListener(result => { 32 | if (result) { 33 | chrome.tabs.get(result.tabId, listenForIconChange); 34 | } 35 | }); 36 | 37 | // listen to messages from other parts of an extension 38 | chrome.runtime.onMessage.addListener(request => { 39 | if (request === "check current") { 40 | chrome.tabs.query( 41 | { 42 | active: true, 43 | currentWindow: true 44 | }, 45 | result => { 46 | if (result) { 47 | chrome.tabs.get(result[0].id, listenForIconChange); 48 | } 49 | } 50 | ); 51 | } 52 | }); 53 | -------------------------------------------------------------------------------- /src/icons/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pawelgrzybek/PinBuddy/c07f6b27313acc303bceb8b2d1174d5696e6a94a/src/icons/icon-128.png -------------------------------------------------------------------------------- /src/icons/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pawelgrzybek/PinBuddy/c07f6b27313acc303bceb8b2d1174d5696e6a94a/src/icons/icon-16.png -------------------------------------------------------------------------------- /src/icons/icon-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pawelgrzybek/PinBuddy/c07f6b27313acc303bceb8b2d1174d5696e6a94a/src/icons/icon-32.png -------------------------------------------------------------------------------- /src/icons/icon-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pawelgrzybek/PinBuddy/c07f6b27313acc303bceb8b2d1174d5696e6a94a/src/icons/icon-48.png -------------------------------------------------------------------------------- /src/icons/icon-active-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pawelgrzybek/PinBuddy/c07f6b27313acc303bceb8b2d1174d5696e6a94a/src/icons/icon-active-128.png -------------------------------------------------------------------------------- /src/icons/icon-active-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pawelgrzybek/PinBuddy/c07f6b27313acc303bceb8b2d1174d5696e6a94a/src/icons/icon-active-16.png -------------------------------------------------------------------------------- /src/icons/icon-active-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pawelgrzybek/PinBuddy/c07f6b27313acc303bceb8b2d1174d5696e6a94a/src/icons/icon-active-32.png -------------------------------------------------------------------------------- /src/icons/icon-active-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pawelgrzybek/PinBuddy/c07f6b27313acc303bceb8b2d1174d5696e6a94a/src/icons/icon-active-48.png -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 2, 3 | "name": "__MSG_extensionName__", 4 | "version": "1.0.7", 5 | 6 | "default_locale": "en", 7 | "description": "__MSG_extensionDescription__", 8 | "icons": { 9 | "16": "icons/icon-16.png", 10 | "32": "icons/icon-32.png", 11 | "48": "icons/icon-48.png", 12 | "128": "icons/icon-128.png" 13 | }, 14 | 15 | "browser_action": { 16 | "default_icon": { 17 | "16": "icons/icon-16.png", 18 | "32": "icons/icon-32.png", 19 | "48": "icons/icon-48.png", 20 | "128": "icons/icon-128.png" 21 | }, 22 | "default_title": "__MSG_extensionName__", 23 | "default_popup": "popup/popup.html" 24 | }, 25 | 26 | "author": "Pawel Grzybek (https://pawelgrzybek.com/)", 27 | "background": { 28 | "scripts": ["background/background.js"], 29 | "persistent": false 30 | }, 31 | "commands": { 32 | "_execute_browser_action": { 33 | "suggested_key": { 34 | "default": "Alt+P", 35 | "mac": "Alt+P" 36 | } 37 | } 38 | }, 39 | "offline_enabled": false, 40 | "options_page": "options/options.html", 41 | "permissions": ["storage", "tabs", "", "notifications"] 42 | } 43 | -------------------------------------------------------------------------------- /src/options/components/App/App.css: -------------------------------------------------------------------------------- 1 | .app { 2 | display: flex; 3 | flex-direction: column; 4 | width: 100%; 5 | max-width: calc(var(--lh) * 22); 6 | margin: 0 auto; 7 | padding: var(--lh); 8 | min-height: 100vh; 9 | } 10 | 11 | .app__header { 12 | animation-name: fadeIn; 13 | animation-duration: var(--animation-duration); 14 | animation-fill-mode: backwards; 15 | } 16 | 17 | .app__main > * { 18 | animation-name: fadeIn; 19 | animation-duration: var(--animation-duration); 20 | animation-fill-mode: backwards; 21 | } 22 | 23 | .app__footer { 24 | margin-top: auto; 25 | animation-name: fadeIn; 26 | animation-duration: var(--animation-duration); 27 | animation-fill-mode: backwards; 28 | } 29 | -------------------------------------------------------------------------------- /src/options/components/App/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import PropTypes from "prop-types"; 3 | import { connect } from "react-redux"; 4 | 5 | import { userFetchFromChromeStorageAction } from "redux-options/actions/user"; 6 | import { 7 | checkConnection, 8 | wentOffline, 9 | wentOnline 10 | } from "redux-options/actions/online"; 11 | import Footer from "./components/Footer"; 12 | import Online from "./components/Online"; 13 | import Offline from "./components/Offline"; 14 | import Logo from "./components/Logo"; 15 | import "./App.css"; 16 | 17 | export class App extends Component { 18 | componentDidMount() { 19 | window.addEventListener("online", this.handleOnlineEvent); 20 | window.addEventListener("offline", this.handleOfflineEvent); 21 | 22 | this.props.checkConnection(navigator.onLine); 23 | this.props.userFetchFromChromeStorage(); 24 | } 25 | 26 | componentWillUnmount() { 27 | window.removeEventListener("online", this.handleOnlineEvent); 28 | window.removeEventListener("offline", this.handleOfflineEvent); 29 | } 30 | 31 | render() { 32 | return ( 33 |
34 |
35 | 36 |
37 | 38 |
39 | {this.props.online ? : } 40 |
41 | 42 |
43 |
44 |
45 |
46 | ); 47 | } 48 | 49 | handleOnlineEvent = () => { 50 | this.props.wentOnline(); 51 | }; 52 | 53 | handleOfflineEvent = () => { 54 | this.props.wentOffline(); 55 | }; 56 | } 57 | 58 | App.propTypes = { 59 | online: PropTypes.bool.isRequired, 60 | userFetchFromChromeStorage: PropTypes.func.isRequired, 61 | checkConnection: PropTypes.func.isRequired, 62 | wentOnline: PropTypes.func.isRequired, 63 | wentOffline: PropTypes.func.isRequired 64 | }; 65 | 66 | const mapStateToProps = state => ({ 67 | online: state.online 68 | }); 69 | 70 | const mapDispatchToProps = dispatch => { 71 | return { 72 | userFetchFromChromeStorage: userInfo => 73 | dispatch(userFetchFromChromeStorageAction(userInfo)), 74 | checkConnection: online => dispatch(checkConnection(online)), 75 | wentOffline: () => dispatch(wentOffline()), 76 | wentOnline: () => dispatch(wentOnline()) 77 | }; 78 | }; 79 | 80 | export default connect( 81 | mapStateToProps, 82 | mapDispatchToProps 83 | )(App); 84 | -------------------------------------------------------------------------------- /src/options/components/App/__tests__/App.test.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { shallow } from "enzyme"; 3 | 4 | import { App } from "../App"; 5 | import Online from "../components/Online"; 6 | import Offline from "../components/Offline"; 7 | 8 | describe("", () => { 9 | const defaultProps = { 10 | online: true, 11 | userFetchFromChromeStorage: jest.fn(), 12 | checkConnection: jest.fn(), 13 | wentOnline: jest.fn(), 14 | wentOffline: jest.fn() 15 | }; 16 | 17 | it("should render correctly", () => { 18 | const tree = shallow(); 19 | expect(tree).toMatchSnapshot(); 20 | }); 21 | 22 | it("should render Online view when online", () => { 23 | const tree = shallow(); 24 | expect(tree.contains()).toBeTruthy(); 25 | }); 26 | 27 | it("should render Offline view when offline", () => { 28 | const tree = shallow(); 29 | expect(tree.contains()).toBeTruthy(); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /src/options/components/App/__tests__/__snapshots__/App.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[` should render correctly 1`] = ` 4 | ShallowWrapper { 5 | Symbol(enzyme.__root__): [Circular], 6 | Symbol(enzyme.__unrendered__): , 39 | Symbol(enzyme.__renderer__): Object { 40 | "batchedUpdates": [Function], 41 | "getNode": [Function], 42 | "render": [Function], 43 | "simulateError": [Function], 44 | "simulateEvent": [Function], 45 | "unmount": [Function], 46 | }, 47 | Symbol(enzyme.__node__): Object { 48 | "instance": null, 49 | "key": undefined, 50 | "nodeType": "host", 51 | "props": Object { 52 | "children": Array [ 53 |
56 | 57 |
, 58 |
61 | 62 |
, 63 |