mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 09:52:26 +01:00
commit
d24acc5f13
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,6 @@
|
|||||||
dist
|
dist
|
||||||
|
|
||||||
|
npm-debug.log
|
||||||
node_modules
|
node_modules
|
||||||
temp
|
temp
|
||||||
.tmp
|
.tmp
|
||||||
@ -15,3 +16,4 @@ app/.DS_Store
|
|||||||
development/bundle.js
|
development/bundle.js
|
||||||
builds.zip
|
builds.zip
|
||||||
test/integration/bundle.js
|
test/integration/bundle.js
|
||||||
|
development/states.js
|
||||||
|
@ -9,6 +9,8 @@
|
|||||||
## 2.13.11 2016-11-23
|
## 2.13.11 2016-11-23
|
||||||
|
|
||||||
- Add support for synchronous RPC method "eth_uninstallFilter".
|
- Add support for synchronous RPC method "eth_uninstallFilter".
|
||||||
|
- Add support for notices.
|
||||||
|
- Add support for working links.
|
||||||
|
|
||||||
## 2.13.10 2016-11-22
|
## 2.13.10 2016-11-22
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ const extend = require('xtend')
|
|||||||
const EthStore = require('eth-store')
|
const EthStore = require('eth-store')
|
||||||
const MetaMaskProvider = require('web3-provider-engine/zero.js')
|
const MetaMaskProvider = require('web3-provider-engine/zero.js')
|
||||||
const IdentityStore = require('./lib/idStore')
|
const IdentityStore = require('./lib/idStore')
|
||||||
|
const NoticeController = require('./notice-controller')
|
||||||
const messageManager = require('./lib/message-manager')
|
const messageManager = require('./lib/message-manager')
|
||||||
const HostStore = require('./lib/remote-store.js').HostStore
|
const HostStore = require('./lib/remote-store.js').HostStore
|
||||||
const Web3 = require('web3')
|
const Web3 = require('web3')
|
||||||
@ -17,6 +18,13 @@ module.exports = class MetamaskController {
|
|||||||
this.idStore = new IdentityStore({
|
this.idStore = new IdentityStore({
|
||||||
configManager: this.configManager,
|
configManager: this.configManager,
|
||||||
})
|
})
|
||||||
|
// notices
|
||||||
|
this.noticeController = new NoticeController({
|
||||||
|
configManager: this.configManager,
|
||||||
|
})
|
||||||
|
this.noticeController.updateNoticesList()
|
||||||
|
// to be uncommented when retrieving notices from a remote server.
|
||||||
|
// this.noticeController.startPolling()
|
||||||
this.provider = this.initializeProvider(opts)
|
this.provider = this.initializeProvider(opts)
|
||||||
this.ethStore = new EthStore(this.provider)
|
this.ethStore = new EthStore(this.provider)
|
||||||
this.idStore.setStore(this.ethStore)
|
this.idStore.setStore(this.ethStore)
|
||||||
@ -30,19 +38,20 @@ module.exports = class MetamaskController {
|
|||||||
this.checkTOSChange()
|
this.checkTOSChange()
|
||||||
|
|
||||||
this.scheduleConversionInterval()
|
this.scheduleConversionInterval()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getState () {
|
getState () {
|
||||||
return extend(
|
return extend(
|
||||||
this.ethStore.getState(),
|
this.ethStore.getState(),
|
||||||
this.idStore.getState(),
|
this.idStore.getState(),
|
||||||
this.configManager.getConfig()
|
this.configManager.getConfig(),
|
||||||
|
this.noticeController.getState()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
getApi () {
|
getApi () {
|
||||||
const idStore = this.idStore
|
const idStore = this.idStore
|
||||||
|
const noticeController = this.noticeController
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getState: (cb) => { cb(null, this.getState()) },
|
getState: (cb) => { cb(null, this.getState()) },
|
||||||
@ -77,6 +86,9 @@ module.exports = class MetamaskController {
|
|||||||
buyEth: this.buyEth.bind(this),
|
buyEth: this.buyEth.bind(this),
|
||||||
// shapeshift
|
// shapeshift
|
||||||
createShapeShiftTx: this.createShapeShiftTx.bind(this),
|
createShapeShiftTx: this.createShapeShiftTx.bind(this),
|
||||||
|
// notices
|
||||||
|
checkNotices: noticeController.updateNoticesList.bind(noticeController),
|
||||||
|
markNoticeRead: noticeController.markNoticeRead.bind(noticeController),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -271,7 +283,7 @@ module.exports = class MetamaskController {
|
|||||||
setTOSHash (hash) {
|
setTOSHash (hash) {
|
||||||
try {
|
try {
|
||||||
this.configManager.setTOSHash(hash)
|
this.configManager.setTOSHash(hash)
|
||||||
} catch (e) {
|
} catch (err) {
|
||||||
console.error('Error in setting terms of service hash.')
|
console.error('Error in setting terms of service hash.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -283,26 +295,28 @@ module.exports = class MetamaskController {
|
|||||||
this.resetDisclaimer()
|
this.resetDisclaimer()
|
||||||
this.setTOSHash(global.TOS_HASH)
|
this.setTOSHash(global.TOS_HASH)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (err) {
|
||||||
console.error('Error in checking TOS change.')
|
console.error('Error in checking TOS change.')
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// disclaimer
|
||||||
|
|
||||||
agreeToDisclaimer (cb) {
|
agreeToDisclaimer (cb) {
|
||||||
try {
|
try {
|
||||||
this.configManager.setConfirmed(true)
|
this.configManager.setConfirmed(true)
|
||||||
cb()
|
cb()
|
||||||
} catch (e) {
|
} catch (err) {
|
||||||
cb(e)
|
cb(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resetDisclaimer () {
|
resetDisclaimer () {
|
||||||
try {
|
try {
|
||||||
this.configManager.setConfirmed(false)
|
this.configManager.setConfirmed(false)
|
||||||
} catch (e) {
|
} catch (err) {
|
||||||
console.error(e)
|
console.error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,8 +331,8 @@ module.exports = class MetamaskController {
|
|||||||
conversionDate: this.configManager.getConversionDate(),
|
conversionDate: this.configManager.getConversionDate(),
|
||||||
}
|
}
|
||||||
cb(data)
|
cb(data)
|
||||||
} catch (e) {
|
} catch (err) {
|
||||||
cb(null, e)
|
cb(null, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -335,8 +349,8 @@ module.exports = class MetamaskController {
|
|||||||
try {
|
try {
|
||||||
this.configManager.setShouldntShowWarning()
|
this.configManager.setShouldntShowWarning()
|
||||||
cb()
|
cb()
|
||||||
} catch (e) {
|
} catch (err) {
|
||||||
cb(e)
|
cb(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -381,8 +395,8 @@ module.exports = class MetamaskController {
|
|||||||
try {
|
try {
|
||||||
this.configManager.setGasMultiplier(gasMultiplier)
|
this.configManager.setGasMultiplier(gasMultiplier)
|
||||||
cb()
|
cb()
|
||||||
} catch (e) {
|
} catch (err) {
|
||||||
cb(e)
|
cb(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
96
app/scripts/notice-controller.js
Normal file
96
app/scripts/notice-controller.js
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
const EventEmitter = require('events').EventEmitter
|
||||||
|
const hardCodedNotices = require('../../development/notices.json')
|
||||||
|
|
||||||
|
module.exports = class NoticeController extends EventEmitter {
|
||||||
|
|
||||||
|
constructor (opts) {
|
||||||
|
super()
|
||||||
|
this.configManager = opts.configManager
|
||||||
|
this.noticePoller = null
|
||||||
|
}
|
||||||
|
|
||||||
|
getState() {
|
||||||
|
var lastUnreadNotice = this.getLatestUnreadNotice()
|
||||||
|
|
||||||
|
return {
|
||||||
|
lastUnreadNotice: lastUnreadNotice,
|
||||||
|
noActiveNotices: !lastUnreadNotice,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getNoticesList() {
|
||||||
|
var data = this.configManager.getData()
|
||||||
|
if ('noticesList' in data) {
|
||||||
|
return data.noticesList
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setNoticesList(list) {
|
||||||
|
var data = this.configManager.getData()
|
||||||
|
data.noticesList = list
|
||||||
|
this.configManager.setData(data)
|
||||||
|
return Promise.resolve(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
markNoticeRead(notice, cb) {
|
||||||
|
cb = cb || function(err){ if (err) throw err }
|
||||||
|
try {
|
||||||
|
var notices = this.getNoticesList()
|
||||||
|
var id = notice.id
|
||||||
|
notices[id].read = true
|
||||||
|
this.setNoticesList(notices)
|
||||||
|
let latestNotice = this.getLatestUnreadNotice()
|
||||||
|
cb(null, latestNotice)
|
||||||
|
} catch (err) {
|
||||||
|
cb(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
updateNoticesList() {
|
||||||
|
return this._retrieveNoticeData().then((newNotices) => {
|
||||||
|
var oldNotices = this.getNoticesList()
|
||||||
|
var combinedNotices = this._mergeNotices(oldNotices, newNotices)
|
||||||
|
return Promise.resolve(this.setNoticesList(combinedNotices))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
getLatestUnreadNotice() {
|
||||||
|
var notices = this.getNoticesList()
|
||||||
|
var filteredNotices = notices.filter((notice) => {
|
||||||
|
return notice.read === false
|
||||||
|
})
|
||||||
|
return filteredNotices[filteredNotices.length - 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
startPolling () {
|
||||||
|
if (this.noticePoller) {
|
||||||
|
clearInterval(this.noticePoller)
|
||||||
|
}
|
||||||
|
this.noticePoller = setInterval(() => {
|
||||||
|
this.noticeController.updateNoticesList()
|
||||||
|
}, 300000)
|
||||||
|
}
|
||||||
|
|
||||||
|
_mergeNotices(oldNotices, newNotices) {
|
||||||
|
var noticeMap = this._mapNoticeIds(oldNotices)
|
||||||
|
newNotices.forEach((notice) => {
|
||||||
|
if (noticeMap.indexOf(notice.id) === -1) {
|
||||||
|
oldNotices.push(notice)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return oldNotices
|
||||||
|
}
|
||||||
|
|
||||||
|
_mapNoticeIds(notices) {
|
||||||
|
return notices.map((notice) => notice.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
_retrieveNoticeData() {
|
||||||
|
// Placeholder for the API.
|
||||||
|
return Promise.resolve(hardCodedNotices)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
36
development/notice-generator.js
Normal file
36
development/notice-generator.js
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
var fsp = require('fs-promise')
|
||||||
|
var path = require('path')
|
||||||
|
var prompt = require('prompt')
|
||||||
|
var open = require('open')
|
||||||
|
var extend = require('extend')
|
||||||
|
var notices = require('./notices.json')
|
||||||
|
|
||||||
|
var id = 0
|
||||||
|
var date = new Date().toDateString()
|
||||||
|
|
||||||
|
var notice = {
|
||||||
|
read: false,
|
||||||
|
date: date,
|
||||||
|
}
|
||||||
|
|
||||||
|
fsp.readdir('notices')
|
||||||
|
.then((files) => {
|
||||||
|
files.forEach(file => { id ++ })
|
||||||
|
Promise.resolve()
|
||||||
|
}).then(() => {
|
||||||
|
fsp.writeFile(`notices/notice_${id}.md`,'Message goes here. Please write out your notice and save before proceeding at the command line.')
|
||||||
|
.then(() => {
|
||||||
|
open(`notices/notice_${id}.md`)
|
||||||
|
prompt.start()
|
||||||
|
prompt.get(['title'], (err, result) => {
|
||||||
|
notice.title = result.title
|
||||||
|
fsp.readFile(`notices/notice_${id}.md`)
|
||||||
|
.then((body) => {
|
||||||
|
notice.body = body.toString()
|
||||||
|
notice.id = id
|
||||||
|
notices.push(notice)
|
||||||
|
return fsp.writeFile(`development/notices.json`, JSON.stringify(notices))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
1
development/notices.json
Normal file
1
development/notices.json
Normal file
@ -0,0 +1 @@
|
|||||||
|
[{"read":false,"date":"Fri Dec 16 2016","title":"Ending Morden Support","body":"Due to [recent events](https://blog.ethereum.org/2016/11/20/from-morden-to-ropsten/), MetaMask is now deprecating support for the Morden Test Network.\n\nUsers will still be able to access Morden through a locally hosted node, but we will no longer be providing hosted access to this network through [Infura](http://infura.io/).\n\nPlease use the new Ropsten Network as your new default test network.\n\nYou can fund your Ropsten account using the buy button on your account page.\n\nBest wishes!\nThe MetaMask Team\n\n","id":0}]
|
File diff suppressed because one or more lines are too long
62
development/states/notice.json
Normal file
62
development/states/notice.json
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
{
|
||||||
|
"metamask": {
|
||||||
|
"isInitialized": true,
|
||||||
|
"isUnlocked": true,
|
||||||
|
"identities": {
|
||||||
|
"0x24a1d059462456aa332d6da9117aa7f91a46f2ac": {
|
||||||
|
"address": "0x24a1d059462456aa332d6da9117aa7f91a46f2ac",
|
||||||
|
"name": "Account 1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"unconfTxs": {},
|
||||||
|
"currentFiat": "USD",
|
||||||
|
"conversionRate": 8.3533002,
|
||||||
|
"conversionDate": 1481671082,
|
||||||
|
"noActiveNotices": false,
|
||||||
|
"lastUnreadNotice": {
|
||||||
|
"read": false,
|
||||||
|
"date": "Tue Dec 13 2016",
|
||||||
|
"title": "MultiVault Support",
|
||||||
|
"body": "# Multi\n# Line\n## Support\nLorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi tincidunt dapibus justo a auctor. Sed luctus metus non mi laoreet, sit amet placerat nibh ultricies. Cras fringilla, urna sit amet sodales porttitor, lacus risus lacinia lorem, non euismod magna felis id ex. Nam iaculis, ante nec imperdiet suscipit, nisi quam fringilla nisl, sed fringilla turpis lectus et nibh. Pellentesque sed neque pretium nulla elementum lacinia eu eget felis. Nulla facilisi. Pellentesque id mi tempor, tempus sapien id, ultricies nibh. Integer faucibus elit non orci dapibus porttitor. Pellentesque rutrum hendrerit sapien ut lacinia. Nunc elementum eget arcu eu volutpat. Integer ullamcorper aliquam metus, eu malesuada tellus vestibulum a.\n",
|
||||||
|
"id": 0
|
||||||
|
},
|
||||||
|
"network": "3",
|
||||||
|
"accounts": {
|
||||||
|
"0x24a1d059462456aa332d6da9117aa7f91a46f2ac": {
|
||||||
|
"code": "0x",
|
||||||
|
"nonce": "0x0",
|
||||||
|
"balance": "0x0",
|
||||||
|
"address": "0x24a1d059462456aa332d6da9117aa7f91a46f2ac"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"transactions": [],
|
||||||
|
"provider": {
|
||||||
|
"type": "testnet"
|
||||||
|
},
|
||||||
|
"selectedAccount": "0x24a1d059462456aa332d6da9117aa7f91a46f2ac",
|
||||||
|
"seedWords": null,
|
||||||
|
"isDisclaimerConfirmed": true,
|
||||||
|
"unconfMsgs": {},
|
||||||
|
"messages": [],
|
||||||
|
"shapeShiftTxList": [],
|
||||||
|
"keyringTypes": [
|
||||||
|
"Simple Key Pair",
|
||||||
|
"HD Key Tree"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"appState": {
|
||||||
|
"menuOpen": false,
|
||||||
|
"currentView": {
|
||||||
|
"name": "accountDetail",
|
||||||
|
"detailView": null,
|
||||||
|
"context": "0x24a1d059462456aa332d6da9117aa7f91a46f2ac"
|
||||||
|
},
|
||||||
|
"accountDetail": {
|
||||||
|
"subview": "transactions"
|
||||||
|
},
|
||||||
|
"transForward": true,
|
||||||
|
"isLoading": false,
|
||||||
|
"warning": null
|
||||||
|
},
|
||||||
|
"identities": {}
|
||||||
|
}
|
40
development/states/terms-and-conditions.json
Normal file
40
development/states/terms-and-conditions.json
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"metamask": {
|
||||||
|
"isInitialized": false,
|
||||||
|
"isUnlocked": false,
|
||||||
|
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
||||||
|
"identities": {},
|
||||||
|
"unconfTxs": {},
|
||||||
|
"currentFiat": "USD",
|
||||||
|
"conversionRate": 8.18703468,
|
||||||
|
"conversionDate": 1481755832,
|
||||||
|
"network": "3",
|
||||||
|
"accounts": {},
|
||||||
|
"transactions": [],
|
||||||
|
"provider": {
|
||||||
|
"type": "testnet"
|
||||||
|
},
|
||||||
|
"isDisclaimerConfirmed": false,
|
||||||
|
"unconfMsgs": {},
|
||||||
|
"messages": [],
|
||||||
|
"shapeShiftTxList": [],
|
||||||
|
"keyringTypes": [
|
||||||
|
"Simple Key Pair",
|
||||||
|
"HD Key Tree"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"appState": {
|
||||||
|
"menuOpen": false,
|
||||||
|
"currentView": {
|
||||||
|
"name": "accounts",
|
||||||
|
"detailView": null
|
||||||
|
},
|
||||||
|
"accountDetail": {
|
||||||
|
"subview": "transactions"
|
||||||
|
},
|
||||||
|
"transForward": true,
|
||||||
|
"isLoading": false,
|
||||||
|
"warning": null
|
||||||
|
},
|
||||||
|
"identities": {}
|
||||||
|
}
|
12
notices/notice_0.md
Normal file
12
notices/notice_0.md
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
Due to [recent events](https://blog.ethereum.org/2016/11/20/from-morden-to-ropsten/), MetaMask is now deprecating support for the Morden Test Network.
|
||||||
|
|
||||||
|
Users will still be able to access Morden through a locally hosted node, but we will no longer be providing hosted access to this network through [Infura](http://infura.io/).
|
||||||
|
|
||||||
|
Please use the new Ropsten Network as your new default test network.
|
||||||
|
|
||||||
|
You can fund your Ropsten account using the buy button on your account page.
|
||||||
|
|
||||||
|
Best wishes!
|
||||||
|
|
||||||
|
The MetaMask Team
|
||||||
|
|
13
package.json
13
package.json
@ -11,12 +11,14 @@
|
|||||||
"test": "npm run fastTest && npm run ci && npm run lint",
|
"test": "npm run fastTest && npm run ci && npm run lint",
|
||||||
"fastTest": "mocha --require test/helper.js --compilers js:babel-register --recursive \"test/unit/**/*.js\"",
|
"fastTest": "mocha --require test/helper.js --compilers js:babel-register --recursive \"test/unit/**/*.js\"",
|
||||||
"watch": "mocha watch --compilers js:babel-register --recursive \"test/unit/**/*.js\"",
|
"watch": "mocha watch --compilers js:babel-register --recursive \"test/unit/**/*.js\"",
|
||||||
"ui": "node development/genStates.js && beefy ui-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
|
"genStates": "node development/genStates.js",
|
||||||
|
"ui": "npm run genStates && beefy ui-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
|
||||||
"mock": "beefy mock-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
|
"mock": "beefy mock-dev.js:bundle.js --live --open --index=./development/index.html --cwd ./",
|
||||||
"buildMock": "browserify ./mock-dev.js -o ./development/bundle.js",
|
"buildMock": "npm run genStates && browserify ./mock-dev.js -o ./development/bundle.js",
|
||||||
"testem": "npm run buildMock && testem",
|
"testem": "npm run buildMock && testem",
|
||||||
"ci": "npm run buildMock && testem ci -P 2",
|
"ci": "npm run buildMock && testem ci -P 2",
|
||||||
"announce": "node development/announcer.js"
|
"announce": "node development/announcer.js",
|
||||||
|
"generateNotice": "node development/notice-generator.js"
|
||||||
},
|
},
|
||||||
"browserify": {
|
"browserify": {
|
||||||
"transform": [
|
"transform": [
|
||||||
@ -47,6 +49,8 @@
|
|||||||
"ethereumjs-tx": "^1.0.0",
|
"ethereumjs-tx": "^1.0.0",
|
||||||
"ethereumjs-util": "^4.4.0",
|
"ethereumjs-util": "^4.4.0",
|
||||||
"express": "^4.14.0",
|
"express": "^4.14.0",
|
||||||
|
"extension-link-enabler": "^1.0.0",
|
||||||
|
"extensionizer": "^1.0.0",
|
||||||
"gulp-eslint": "^2.0.0",
|
"gulp-eslint": "^2.0.0",
|
||||||
"hat": "0.0.3",
|
"hat": "0.0.3",
|
||||||
"identicon.js": "^1.2.1",
|
"identicon.js": "^1.2.1",
|
||||||
@ -97,6 +101,7 @@
|
|||||||
"chai": "^3.5.0",
|
"chai": "^3.5.0",
|
||||||
"deep-freeze-strict": "^1.1.1",
|
"deep-freeze-strict": "^1.1.1",
|
||||||
"del": "^2.2.0",
|
"del": "^2.2.0",
|
||||||
|
"fs-promise": "^1.0.0",
|
||||||
"gulp": "github:gulpjs/gulp#4.0",
|
"gulp": "github:gulpjs/gulp#4.0",
|
||||||
"gulp-brfs": "^0.1.0",
|
"gulp-brfs": "^0.1.0",
|
||||||
"gulp-if": "^2.0.1",
|
"gulp-if": "^2.0.1",
|
||||||
@ -116,6 +121,8 @@
|
|||||||
"mocha-jsdom": "^1.1.0",
|
"mocha-jsdom": "^1.1.0",
|
||||||
"mocha-sinon": "^1.1.5",
|
"mocha-sinon": "^1.1.5",
|
||||||
"nock": "^8.0.0",
|
"nock": "^8.0.0",
|
||||||
|
"open": "0.0.5",
|
||||||
|
"prompt": "^1.0.0",
|
||||||
"qs": "^6.2.0",
|
"qs": "^6.2.0",
|
||||||
"qunit": "^0.9.1",
|
"qunit": "^0.9.1",
|
||||||
"sinon": "^1.17.3",
|
"sinon": "^1.17.3",
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
const assert = require('assert')
|
const assert = require('assert')
|
||||||
const extend = require('xtend')
|
const extend = require('xtend')
|
||||||
const STORAGE_KEY = 'metamask-persistance-key'
|
|
||||||
var configManagerGen = require('../lib/mock-config-manager')
|
|
||||||
var configManager
|
|
||||||
const rp = require('request-promise')
|
const rp = require('request-promise')
|
||||||
const nock = require('nock')
|
const nock = require('nock')
|
||||||
|
var configManagerGen = require('../lib/mock-config-manager')
|
||||||
|
const STORAGE_KEY = 'metamask-persistance-key'
|
||||||
|
|
||||||
describe('config-manager', function() {
|
describe('config-manager', function() {
|
||||||
|
var configManager
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
window.localStorage = {} // Hacking localStorage support into JSDom
|
window.localStorage = {} // Hacking localStorage support into JSDom
|
||||||
|
115
test/unit/notice-controller-test.js
Normal file
115
test/unit/notice-controller-test.js
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
const assert = require('assert')
|
||||||
|
const extend = require('xtend')
|
||||||
|
const rp = require('request-promise')
|
||||||
|
const nock = require('nock')
|
||||||
|
const configManagerGen = require('../lib/mock-config-manager')
|
||||||
|
const NoticeController = require('../../app/scripts/notice-controller')
|
||||||
|
const STORAGE_KEY = 'metamask-persistance-key'
|
||||||
|
// Hacking localStorage support into JSDom
|
||||||
|
window.localStorage = {}
|
||||||
|
|
||||||
|
describe('notice-controller', function() {
|
||||||
|
var noticeController
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
let configManager = configManagerGen()
|
||||||
|
noticeController = new NoticeController({
|
||||||
|
configManager: configManager,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('notices', function() {
|
||||||
|
describe('#getNoticesList', function() {
|
||||||
|
it('should return an empty array when new', function() {
|
||||||
|
var testList = [{
|
||||||
|
id:0,
|
||||||
|
read:false,
|
||||||
|
title:"Futuristic Notice"
|
||||||
|
}]
|
||||||
|
var result = noticeController.getNoticesList()
|
||||||
|
assert.equal(result.length, 0)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('#setNoticesList', function() {
|
||||||
|
it('should set data appropriately', function () {
|
||||||
|
var testList = [{
|
||||||
|
id:0,
|
||||||
|
read:false,
|
||||||
|
title:"Futuristic Notice"
|
||||||
|
}]
|
||||||
|
noticeController.setNoticesList(testList)
|
||||||
|
var testListId = noticeController.getNoticesList()[0].id
|
||||||
|
assert.equal(testListId, 0)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('#updateNoticeslist', function() {
|
||||||
|
it('should integrate the latest changes from the source', function() {
|
||||||
|
var testList = [{
|
||||||
|
id:55,
|
||||||
|
read:false,
|
||||||
|
title:"Futuristic Notice"
|
||||||
|
}]
|
||||||
|
noticeController.setNoticesList(testList)
|
||||||
|
noticeController.updateNoticesList().then(() => {
|
||||||
|
var newList = noticeController.getNoticesList()
|
||||||
|
assert.ok(newList[0].id === 55)
|
||||||
|
assert.ok(newList[1])
|
||||||
|
})
|
||||||
|
})
|
||||||
|
it('should not overwrite any existing fields', function () {
|
||||||
|
var testList = [{
|
||||||
|
id:0,
|
||||||
|
read:false,
|
||||||
|
title:"Futuristic Notice"
|
||||||
|
}]
|
||||||
|
noticeController.setNoticesList(testList)
|
||||||
|
noticeController.updateNoticesList().then(() => {
|
||||||
|
var newList = noticeController.getNoticesList()
|
||||||
|
assert.equal(newList[0].id, 0)
|
||||||
|
assert.equal(newList[0].title, "Futuristic Notice")
|
||||||
|
assert.equal(newList.length, 1)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('#markNoticeRead', function () {
|
||||||
|
it('should mark a notice as read', function () {
|
||||||
|
var testList = [{
|
||||||
|
id:0,
|
||||||
|
read:false,
|
||||||
|
title:"Futuristic Notice"
|
||||||
|
}]
|
||||||
|
noticeController.setNoticesList(testList)
|
||||||
|
noticeController.markNoticeRead(testList[0])
|
||||||
|
var newList = noticeController.getNoticesList()
|
||||||
|
assert.ok(newList[0].read)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('#getLatestUnreadNotice', function () {
|
||||||
|
it('should retrieve the latest unread notice', function () {
|
||||||
|
var testList = [
|
||||||
|
{id:0,read:true,title:"Past Notice"},
|
||||||
|
{id:1,read:false,title:"Current Notice"},
|
||||||
|
{id:2,read:false,title:"Future Notice"},
|
||||||
|
]
|
||||||
|
noticeController.setNoticesList(testList)
|
||||||
|
var latestUnread = noticeController.getLatestUnreadNotice()
|
||||||
|
assert.equal(latestUnread.id, 2)
|
||||||
|
})
|
||||||
|
it('should return undefined if no unread notices exist.', function () {
|
||||||
|
var testList = [
|
||||||
|
{id:0,read:true,title:"Past Notice"},
|
||||||
|
{id:1,read:true,title:"Current Notice"},
|
||||||
|
{id:2,read:true,title:"Future Notice"},
|
||||||
|
]
|
||||||
|
noticeController.setNoticesList(testList)
|
||||||
|
var latestUnread = noticeController.getLatestUnreadNotice()
|
||||||
|
assert.ok(!latestUnread)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})
|
@ -7,6 +7,13 @@ var actions = {
|
|||||||
// remote state
|
// remote state
|
||||||
UPDATE_METAMASK_STATE: 'UPDATE_METAMASK_STATE',
|
UPDATE_METAMASK_STATE: 'UPDATE_METAMASK_STATE',
|
||||||
updateMetamaskState: updateMetamaskState,
|
updateMetamaskState: updateMetamaskState,
|
||||||
|
// notices
|
||||||
|
MARK_NOTICE_READ: 'MARK_NOTICE_READ',
|
||||||
|
markNoticeRead: markNoticeRead,
|
||||||
|
SHOW_NOTICE: 'SHOW_NOTICE',
|
||||||
|
showNotice: showNotice,
|
||||||
|
CLEAR_NOTICES: 'CLEAR_NOTICES',
|
||||||
|
clearNotices: clearNotices,
|
||||||
// intialize screen
|
// intialize screen
|
||||||
AGREE_TO_DISCLAIMER: 'AGREE_TO_DISCLAIMER',
|
AGREE_TO_DISCLAIMER: 'AGREE_TO_DISCLAIMER',
|
||||||
agreeToDisclaimer: agreeToDisclaimer,
|
agreeToDisclaimer: agreeToDisclaimer,
|
||||||
@ -519,6 +526,43 @@ function goBackToInitView () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// notice
|
||||||
|
//
|
||||||
|
|
||||||
|
function markNoticeRead (notice) {
|
||||||
|
return (dispatch) => {
|
||||||
|
dispatch(this.showLoadingIndication())
|
||||||
|
_accountManager.markNoticeRead(notice, (err, notice) => {
|
||||||
|
dispatch(this.hideLoadingIndication())
|
||||||
|
if (err) {
|
||||||
|
return dispatch(actions.showWarning(err))
|
||||||
|
}
|
||||||
|
if (notice) {
|
||||||
|
return dispatch(actions.showNotice(notice))
|
||||||
|
} else {
|
||||||
|
dispatch(this.clearNotices())
|
||||||
|
return {
|
||||||
|
type: actions.SHOW_ACCOUNTS_PAGE,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showNotice (notice) {
|
||||||
|
return {
|
||||||
|
type: actions.SHOW_NOTICE,
|
||||||
|
value: notice,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearNotices () {
|
||||||
|
return {
|
||||||
|
type: actions.CLEAR_NOTICES,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// config
|
// config
|
||||||
//
|
//
|
||||||
|
@ -17,6 +17,8 @@ const AccountsScreen = require('./accounts')
|
|||||||
const AccountDetailScreen = require('./account-detail')
|
const AccountDetailScreen = require('./account-detail')
|
||||||
const SendTransactionScreen = require('./send')
|
const SendTransactionScreen = require('./send')
|
||||||
const ConfirmTxScreen = require('./conf-tx')
|
const ConfirmTxScreen = require('./conf-tx')
|
||||||
|
// notice
|
||||||
|
const NoticeScreen = require('./notice')
|
||||||
// other views
|
// other views
|
||||||
const ConfigScreen = require('./config')
|
const ConfigScreen = require('./config')
|
||||||
const RevealSeedConfirmation = require('./recover-seed/confirmation')
|
const RevealSeedConfirmation = require('./recover-seed/confirmation')
|
||||||
@ -41,6 +43,7 @@ function mapStateToProps (state) {
|
|||||||
isLoading: state.appState.isLoading,
|
isLoading: state.appState.isLoading,
|
||||||
isConfirmed: state.metamask.isConfirmed,
|
isConfirmed: state.metamask.isConfirmed,
|
||||||
isEthConfirmed: state.metamask.isEthConfirmed,
|
isEthConfirmed: state.metamask.isEthConfirmed,
|
||||||
|
noActiveNotices: state.metamask.noActiveNotices,
|
||||||
isInitialized: state.metamask.isInitialized,
|
isInitialized: state.metamask.isInitialized,
|
||||||
isUnlocked: state.metamask.isUnlocked,
|
isUnlocked: state.metamask.isUnlocked,
|
||||||
currentView: state.appState.currentView,
|
currentView: state.appState.currentView,
|
||||||
@ -425,6 +428,10 @@ App.prototype.renderPrimary = function () {
|
|||||||
return h(UnlockScreen, {key: 'locked'})
|
return h(UnlockScreen, {key: 'locked'})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!props.noActiveNotices) {
|
||||||
|
return h(NoticeScreen, {key: 'NoticeScreen'})
|
||||||
|
}
|
||||||
|
|
||||||
// show current view
|
// show current view
|
||||||
switch (props.currentView.name) {
|
switch (props.currentView.name) {
|
||||||
case 'EthStoreWarning':
|
case 'EthStoreWarning':
|
||||||
|
@ -6,6 +6,8 @@ const actions = require('../actions')
|
|||||||
const ReactMarkdown = require('react-markdown')
|
const ReactMarkdown = require('react-markdown')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
|
const linker = require('extension-link-enabler')
|
||||||
|
const findDOMNode = require('react-dom').findDOMNode
|
||||||
const disclaimer = fs.readFileSync(path.join(__dirname, '..', '..', '..', 'USER_AGREEMENT.md')).toString()
|
const disclaimer = fs.readFileSync(path.join(__dirname, '..', '..', '..', 'USER_AGREEMENT.md')).toString()
|
||||||
module.exports = connect(mapStateToProps)(DisclaimerScreen)
|
module.exports = connect(mapStateToProps)(DisclaimerScreen)
|
||||||
|
|
||||||
@ -98,3 +100,13 @@ DisclaimerScreen.prototype.render = function () {
|
|||||||
])
|
])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DisclaimerScreen.prototype.componentDidMount = function () {
|
||||||
|
var node = findDOMNode(this)
|
||||||
|
linker.setupListener(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
DisclaimerScreen.prototype.componentWillUnmount = function () {
|
||||||
|
var node = findDOMNode(this)
|
||||||
|
linker.teardownListener(node)
|
||||||
|
}
|
||||||
|
118
ui/app/notice.js
Normal file
118
ui/app/notice.js
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
const inherits = require('util').inherits
|
||||||
|
const Component = require('react').Component
|
||||||
|
const h = require('react-hyperscript')
|
||||||
|
const ReactMarkdown = require('react-markdown')
|
||||||
|
const connect = require('react-redux').connect
|
||||||
|
const actions = require('./actions')
|
||||||
|
const linker = require('extension-link-enabler')
|
||||||
|
const findDOMNode = require('react-dom').findDOMNode
|
||||||
|
|
||||||
|
module.exports = connect(mapStateToProps)(Notice)
|
||||||
|
|
||||||
|
function mapStateToProps (state) {
|
||||||
|
return {
|
||||||
|
lastUnreadNotice: state.metamask.lastUnreadNotice,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inherits(Notice, Component)
|
||||||
|
function Notice () {
|
||||||
|
Component.call(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
Notice.prototype.render = function () {
|
||||||
|
const props = this.props
|
||||||
|
const title = props.lastUnreadNotice.title
|
||||||
|
const date = props.lastUnreadNotice.date
|
||||||
|
|
||||||
|
return (
|
||||||
|
h('.flex-column.flex-center.flex-grow', [
|
||||||
|
h('h3.flex-center.text-transform-uppercacse.terms-header', {
|
||||||
|
style: {
|
||||||
|
background: '#EBEBEB',
|
||||||
|
color: '#AEAEAE',
|
||||||
|
width: '100%',
|
||||||
|
fontSize: '20px',
|
||||||
|
textAlign: 'center',
|
||||||
|
padding: 6,
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
title,
|
||||||
|
]),
|
||||||
|
|
||||||
|
h('h5.flex-center.text-transform-uppercacse.terms-header', {
|
||||||
|
style: {
|
||||||
|
background: '#EBEBEB',
|
||||||
|
color: '#AEAEAE',
|
||||||
|
marginBottom: 24,
|
||||||
|
width: '100%',
|
||||||
|
fontSize: '20px',
|
||||||
|
textAlign: 'center',
|
||||||
|
padding: 6,
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
date,
|
||||||
|
]),
|
||||||
|
|
||||||
|
h('style', `
|
||||||
|
|
||||||
|
.markdown {
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
.markdown h1, .markdown h2, .markdown h3 {
|
||||||
|
margin: 10px 0;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown strong {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
.markdown em {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown p {
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown a {
|
||||||
|
color: #df6b0e;
|
||||||
|
}
|
||||||
|
|
||||||
|
`),
|
||||||
|
|
||||||
|
h('div.markdown', {
|
||||||
|
style: {
|
||||||
|
background: 'rgb(235, 235, 235)',
|
||||||
|
height: '310px',
|
||||||
|
padding: '6px',
|
||||||
|
width: '90%',
|
||||||
|
overflowY: 'scroll',
|
||||||
|
scroll: 'auto',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
h(ReactMarkdown, {
|
||||||
|
source: props.lastUnreadNotice.body,
|
||||||
|
skipHtml: true,
|
||||||
|
}),
|
||||||
|
]),
|
||||||
|
|
||||||
|
h('button', {
|
||||||
|
onClick: () => props.dispatch(actions.markNoticeRead(props.lastUnreadNotice)),
|
||||||
|
style: {
|
||||||
|
marginTop: '18px',
|
||||||
|
},
|
||||||
|
}, 'Continue'),
|
||||||
|
])
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Notice.prototype.componentDidMount = function () {
|
||||||
|
var node = findDOMNode(this)
|
||||||
|
linker.setupListener(node)
|
||||||
|
}
|
||||||
|
|
||||||
|
Notice.prototype.componentWillUnmount = function () {
|
||||||
|
var node = findDOMNode(this)
|
||||||
|
linker.teardownListener(node)
|
||||||
|
}
|
@ -229,6 +229,12 @@ function reduceApp (state, action) {
|
|||||||
scrollToBottom: false,
|
scrollToBottom: false,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
case actions.SHOW_NOTICE:
|
||||||
|
return extend(appState, {
|
||||||
|
transForward: true,
|
||||||
|
isLoading: false,
|
||||||
|
})
|
||||||
|
|
||||||
case actions.REVEAL_ACCOUNT:
|
case actions.REVEAL_ACCOUNT:
|
||||||
return extend(appState, {
|
return extend(appState, {
|
||||||
scrollToBottom: true,
|
scrollToBottom: true,
|
||||||
|
@ -17,6 +17,8 @@ function reduceMetamask (state, action) {
|
|||||||
currentFiat: 'USD',
|
currentFiat: 'USD',
|
||||||
conversionRate: 0,
|
conversionRate: 0,
|
||||||
conversionDate: 'N/A',
|
conversionDate: 'N/A',
|
||||||
|
noActiveNotices: true,
|
||||||
|
lastUnreadNotice: undefined,
|
||||||
}, state.metamask)
|
}, state.metamask)
|
||||||
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
@ -26,6 +28,17 @@ function reduceMetamask (state, action) {
|
|||||||
delete newState.seedWords
|
delete newState.seedWords
|
||||||
return newState
|
return newState
|
||||||
|
|
||||||
|
case actions.SHOW_NOTICE:
|
||||||
|
return extend(metamaskState, {
|
||||||
|
noActiveNotices: false,
|
||||||
|
lastUnreadNotice: action.value,
|
||||||
|
})
|
||||||
|
|
||||||
|
case actions.CLEAR_NOTICES:
|
||||||
|
return extend(metamaskState, {
|
||||||
|
noActiveNotices: true,
|
||||||
|
})
|
||||||
|
|
||||||
case actions.UPDATE_METAMASK_STATE:
|
case actions.UPDATE_METAMASK_STATE:
|
||||||
return extend(metamaskState, action.value)
|
return extend(metamaskState, action.value)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user