The MetaMask UI is essentially just a website that can be configured by passing it the API and state subscriptions from above. Anyone could make a UI that consumes these, effectively reskinning MetaMask.javascript
MetaMask UI本質上只是一個網站,能夠經過從上面傳遞API和狀態訂閱來配置它。任何人均可以建立一個使用這些的UI,有效地從新設計MetaMaskhtml
You can see this in action in our file ui/index.js. There you can see an argument being passed in named accountManager
, which is essentially a MetaMask controller (forgive its really outdated parameter name!). With access to that object, the UI is able to initialize a whole React/Redux app that relies on this API for its account/blockchain-related/persistent states.java
你能夠從文件ui/index.js中看見它的實現。在這裏,您能夠看到一個參數在accountManager中傳遞,accountManager本質上是一個MetaMask控制器(該參數名稱已通過時!)經過對該對象的訪問,UI可以初始化整個依賴於該API實現其賬戶/區塊鏈相關/持久狀態的React/Redux應用程序。react
metamask-extension/ui/index.jsgit
const render = require('react-dom').render const h = require('react-hyperscript') const Root = require('./app/root') const actions = require('./app/actions') const configureStore = require('./app/store') const txHelper = require('./lib/tx-helper') const { fetchLocale } = require('./i18n-helper') const log = require('loglevel') module.exports = launchMetamaskUi log.setLevel(global.METAMASK_DEBUG ? 'debug' : 'warn') function launchMetamaskUi (opts, cb) { var accountManager = opts.accountManager//accountManager就是一個metamask控制器,因此metamask-controller.js中的函數其都能調用,UI通常就調用getApi ()和getState()兩個函數 actions._setBackgroundConnection(accountManager)//設置後臺的鏈接信息 // check if we are unlocked first accountManager.getState(function (err, metamaskState) {//返回一個javascript對象,表示當前的MetaMask狀態 if (err) return cb(err) startApp(metamaskState, accountManager, opts) .then((store) => { cb(null, store) }) }) }
//打開APP async function startApp (metamaskState, accountManager, opts) { // parse opts if (!metamaskState.featureFlags) metamaskState.featureFlags = {} const currentLocaleMessages = metamaskState.currentLocale ? await fetchLocale(metamaskState.currentLocale)//獲得`./_locales/${metamaskState.currentLocale}/messages.json`文件,由於選擇的語言不一樣 : {} const enLocaleMessages = await fetchLocale('en')//這個選擇的是英語 const store = configureStore({//配置metamask環境信息,如中間件等 // metamaskState represents the cross-tab state metamask: metamaskState, // appState represents the current tab's popup state appState: {}, localeMessages: { current: currentLocaleMessages, en: enLocaleMessages, }, // Which blockchain we are using: networkVersion: opts.networkVersion, }) // if unconfirmed txs, start on txConf page
//獲得時間由小到大排序的全部信息 const unapprovedTxsAll = txHelper(metamaskState.unapprovedTxs, metamaskState.unapprovedMsgs, metamaskState.unapprovedPersonalMsgs, metamaskState.unapprovedTypedMessages, metamaskState.network) const numberOfUnapprivedTx = unapprovedTxsAll.length if (numberOfUnapprivedTx > 0) { store.dispatch(actions.showConfTxPage({ id: unapprovedTxsAll[numberOfUnapprivedTx - 1].id,//配置actions中showConfTxPage的值,tx裏的詳細信息能夠看本博客metamask源碼學習-background.js,其的代碼註釋中有寫,如id... })) } accountManager.on('update', function (metamaskState) {//若是metamaskState由更新時觸發 store.dispatch(actions.updateMetamaskState(metamaskState)) }) // global metamask api - used by tooling global.metamask = { updateCurrentLocale: (code) => { store.dispatch(actions.updateCurrentLocale(code)) }, setProviderType: (type) => { store.dispatch(actions.setProviderType(type)) }, } // start app,使用react框架來啓動該metamask app render( h(Root, { // inject initial state store: store,//這裏的內容就是上面進行相應設置後的狀態信息,這個store有的詳細內容的解釋看本博客metamask源碼學習-background.js,其的代碼註釋中有寫本博客metamask源碼學習-background.js,其的代碼註釋中有寫
} ), opts.container) return store }
metamask-extension/app/scripts/metamask-controller.jsgithub
//============================================================================= // EXPOSED TO THE UI SUBSYSTEM //============================================================================= /** * The metamask-state of the various controllers是獲得多種控制器的狀態, made available to the UI * * @returns {Object} status */ getState () { const wallet = this.configManager.getWallet() const vault = this.keyringController.store.getState().vault const isInitialized = (!!wallet || !!vault)//就是隻要二者wallet和vault中有一個有值,那麼就說明初始化過了 return { ...{ isInitialized }, ...this.memStore.getFlatState(), ...this.configManager.getConfig(), ...{ lostAccounts: this.configManager.getLostAccounts(), seedWords: this.configManager.getSeedWords(), forgottenPassword: this.configManager.getPasswordForgotten(), }, } }
!!()。 好比:a默認是undefined,!a是true,!!a則是false,因此b的值是false,而再也不是undefined。這樣寫能夠方便後續判斷使用。 因此,!!(a)的做用是將a強制轉換爲布爾型(boolean)。
metamask-extension/ui/i18n-helper.jsweb
async function fetchLocale (localeName) {//獲得`./_locales/${metamaskState.currentLocale}/messages.json`文件 try { const response = await fetch(`./_locales/${localeName}/messages.json`) return await response.json() } catch (error) { log.error(`failed to fetch ${localeName} locale because of ${error}`) return {} } }
metamask-extension/ui/app/util.jsjson
function valuesFor (obj) {//經過keys()獲得obj對象中的鍵值,由於這裏面的鍵值並非簡單的1,2;而後再根據得到的鍵值去獲得obj中對應的值,並再組合起來成新的obj對象,即map()
if (!obj) return [] return Object.keys(obj) .map(function (key) { return obj[key] }) }
舉例:api
var obj = {'a':'123','b':'345'}; console.log(Object.keys(obj)); //['a','b']
metamask-extension/ui/lib/tx-helper.jsapp
const valuesFor = require('../app/util').valuesFor const log = require('loglevel') module.exports = function (unapprovedTxs, unapprovedMsgs, personalMsgs, typedMessages, network) { log.debug('tx-helper called with params:') log.debug({ unapprovedTxs, unapprovedMsgs, personalMsgs, typedMessages, network })
//就是network不爲0時,那麼就獲得unapprovedTxs中知足metamaskNetworkId === network條件的全部沒被贊同的Txs;若是爲0,則獲得全部沒被贊同的Txs const txValues = network ? valuesFor(unapprovedTxs).filter(txMeta => txMeta.metamaskNetworkId === network) : valuesFor(unapprovedTxs) log.debug(`tx helper found ${txValues.length} unapproved txs`) const msgValues = valuesFor(unapprovedMsgs)//獲得沒有簽名的信息 log.debug(`tx helper found ${msgValues.length} unsigned messages`) let allValues = txValues.concat(msgValues) const personalValues = valuesFor(personalMsgs)//獲得沒有簽名的我的信息 log.debug(`tx helper found ${personalValues.length} unsigned personal messages`) allValues = allValues.concat(personalValues) const typedValues = valuesFor(typedMessages)//獲得沒有簽名的typed信息 log.debug(`tx helper found ${typedValues.length} unsigned typed messages`) allValues = allValues.concat(typedValues) //並所有串聯起來放在allValues中
allValues = allValues.sort((a, b) => {//一個個去對比,按照每一個信息的時間由小到大去排序,若是想要有大到小,就寫成a.time < b.time return a.time > b.time }) return allValues }