Different from its knowledge sharing system, this system is used to solve the dependence of different teams on different websites, and the development of a set of bookmark collaborative management OA system, convenient for the management of multiple public platforms.

Not much BB, say the development ideas + practice:

Shared system design ideas

For example, I have a lot of websites in my team that need to be stored (there are many that haven’t been captured yet…). If a new student comes on board, you need to share these sites one by one. Very inconvenient, the system can be needed to classify the website storage, his team of students into a group, to achieve the purpose of sharing bookmarks.

With this idea in mind, let’s sort out the requirements:

User relationship: administrator, common user.

  • Administrator rights: Add articles, view articles, delete articles, add category labels, delete category labels, remove group members, and dissolve groups
  • Common user rights: Add articles, view articles, and exit the group

UI: Chrome plug-in, background management system

  • Chrome plugin: Register and log in, create/join groups, add articles, log out
  • Background management system: view articles, exit/dissolve groups, add/delete labels, remove group members, dissolve groups

The project build

React learn once. Write anywhere around Ts. Run the command build directly here.

npx create-react-app my-app --template typescript
Copy the code

Once you’re done, create your own directory structure.

├ ─ ─ the config# ts path configuration├ ─ ─ the publicNote: this file is used for information recognition in chrome extensions├ ─ ─ the script# Required bash scripts (described below)├─ SRC │ ├─ apis# interface│ ├ ─ ─ the components# components│ ├ ─ ─ configs# Development/production environment differentiation│ ├ ─ ─ contentScripts# 用于 chrome│ ├ ─ ─ pages# page│ ├ ─ ─ the router# routing│ ├ ─ ─ stores# mbox warehouse│ ├ ─ ─ utils# utility function│ ├─ ├─ ├.tsX │ ├─ ├.config.js# Don't want to eject so modify the configuration items through Craco├ ─ ─ tsconfig. Json# Some TS configuration└ ─ ─ package. JsonCopy the code

Chrome extension open, equivalent to a web page open, closed is a web page closed. So our expansion requires two functions.

  1. Save the position of the TAB bar when the user clicks on it (it cannot be on the first page every time the user opens it, and the click information of the user should be recorded)
  2. Save the current browser web page information (easy to add bookmarks)

There is no need to repeat the normal page writing. So let’s focus on what makes chrome extensions different from web projects in general.

Chrome extensions need to identify extension information: hence the manifest.json file.

{
  "manifest_version": 2."name": "Tnshare"."description": "A plugin for sharing knowledge and common tools"."version": "1.0"."permissions": ["tabs"."storage"]."icons": {
    "322": "icon.png"
  },
  "browser_action": {
    "default_icon": "icon.png"."default_popup": "index.html"
  },
  "content_scripts": [{"matches": ["<all_urls>"]."js": ["./contentScripts/get_data.js"]."run_at": "document_start"}]."content_security_policy": "script-src 'self' 'sha256-hrABjXgkmzJSAYJz7Tb8+vCZlVwt6UMWGfHKxDlE+2k='; object-src 'self'"
}
Copy the code

Here we’ll focus on permissions, content_scripts, and content_security_policy

  • permissions: Permissions we need for development. Tabs are used to expand information interaction with user pages. Strong is similar to localStorage for storing user click information
  • content_scriptsIn order to obtain user information, you must create a JAVASCRIPT script. Chrome will automatically inject this script into the user’s page to obtain user information. (Note: js in content_scripts can only get the HTML/CSS of the user’s page, not its JS content)
  • content_security_policyThe chrome plugin does not allow the use of inline JS by default because webpack will have inline JS in HTML. You can add the javascript explanation form here (or add webpack global configuration to Craco and cancel the inline injection).

In the srciptbash

If you are careful, you can see that content_scripts specifies the location of the JS file that needs to be injected. When packaging, we also need to ensure that the file location is consistent. Since you don’t want to eject, you need to write a bash srcipt that will inject the contents of that folder into the Build directory after packaging is complete.

Chmod +x script/build.sh
build() {
  mkdir -p build/contentScripts
  cp -r src/contentScripts/* build/contentScripts
}
build
Copy the code

utils/chrome_methods.tsTo avoid the development environment does not have chrome global variables

Another problem is that the chrome extension uses Chrome injected global variables. However, in native development, without this variable, the page will report errors, and it would be inelegant to comment the code directly… Therefore, the variables of production environment and development environment need to be separated.

In order to reduce the number of lines, the code content of the TAB TAB click type has been deleted to reduce the repetition, a large pile of code to me I also can’t look at 🥕

import React from "react"
import { stores } from "@/stores/index"
export interface IChromeMethodsProps {
  ChromeTabsQuery: (cb: Function) = > void // Get external links
  ChromeSetTabIdx: (key: React.Key, cb: Function) = > void // Set the TAB click position
  ChromeGetTabIdx: (cb: (tab: string) => void) = > void // Get the TAB TAB click position
  ChromeSetToken: (token: string) = > Promise<string> / / set the token
  ChromeGetToken: () = > Promise<string> / / access token
}

const dev_methods: IChromeMethodsProps = {
  ChromeTabsQuery: cb= > {
    cb({
      title: "i m title".link: "i m link".description: "im description",}}),ChromeSetToken: token= > {
    return new Promise(resolve= > {
      localStorage.setItem("token", token)
      stores.stateStore.handleSetToken(token)
      resolve(token)
    })
  },
  ChromeGetToken: () = > {
    return new Promise(resolve= > {
      const token = localStorage.getItem("token") | |""
      stores.stateStore.handleSetToken(token)
      resolve(token)
    })
  },
}

const prod_methods: IChromeMethodsProps = {
  ChromeTabsQuery(cb) {
    chrome.tabs.query({ active: true.currentWindow: true }, function (tabs) {
      chrome.tabs.sendMessage(tabs[0].id as number, { action: "GET_PAGE_DATA" }, function (response) {
        cb(response)
      })
    })
  },
  ChromeSetToken: token= > {
    return new Promise(resolve= > {
      chrome.storage.local.set({ token }, function () {
        stores.stateStore.handleSetToken(token)
        resolve(token)
      })
    })
  },
  ChromeGetToken: () = > {
    return new Promise(resolve= > {
      chrome.storage.local.get(["token"].function (res) {
        const token = res ? res.token : ""
        stores.stateStore.handleSetToken(token)
        resolve(token)
      })
    })
  },
}

const Methods = process.env.REACT_APP_ENV === "dev" ? dev_methods : prod_methods

export default {
  ...Methods,
}
Copy the code

One thing to note here: Chrome. storage is asynchronous storage (for local I/O speed) and localstorage is synchronous storage. So we need a promise package layer here to make sure that the invocation is consistent.

other

  • In the TS environment, using chrome global variables directly will cause an error. Download one@types/chromeGet its type construct.

The background page

There are no complex difficulties in the background itself, namely regular page development. Here’s the GIF on the image demo, as a feature of a complete project 🤫…

The last

The Chrome plugin documentation and download address are available on my Github.

Because the service is currently running on my own overlord server, it is not open source. If you have open source demand, please like this article. If more, I will open source to share with you, but I will stop the online service on this server.