mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-22 17:33:23 +01:00
Add ability to show notices to user & get confirmation.
Implement generation of markdown for notice files. Create npm command. Enhance notice generation. Add test files to test multiple notices. Add basic markdown support to notices. Interval checks for updates. Add extensionizer and linker Add terms and conditions state file Add link support to disclaimer. Changelog addition.
This commit is contained in:
parent
d5569781ba
commit
8819475a2e
@ -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 Migrator = require('pojo-migrator')
|
|||||||
const MetamaskConfig = require('../config.js')
|
const MetamaskConfig = require('../config.js')
|
||||||
const migrations = require('./migrations')
|
const migrations = require('./migrations')
|
||||||
const rp = require('request-promise')
|
const rp = require('request-promise')
|
||||||
|
const notices = require('../../../development/notices.json')
|
||||||
|
|
||||||
const TESTNET_RPC = MetamaskConfig.network.testnet
|
const TESTNET_RPC = MetamaskConfig.network.testnet
|
||||||
const MAINNET_RPC = MetamaskConfig.network.mainnet
|
const MAINNET_RPC = MetamaskConfig.network.mainnet
|
||||||
@ -161,6 +162,69 @@ ConfigManager.prototype.setData = function (data) {
|
|||||||
this.migrator.saveData(data)
|
this.migrator.saveData(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Notices
|
||||||
|
//
|
||||||
|
|
||||||
|
ConfigManager.prototype.getNoticesList = function () {
|
||||||
|
var data = this.getData()
|
||||||
|
if ('noticesList' in data) {
|
||||||
|
return data.noticesList
|
||||||
|
} else {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigManager.prototype.setNoticesList = function (list) {
|
||||||
|
var data = this.getData()
|
||||||
|
data.noticesList = list
|
||||||
|
this.setData(data)
|
||||||
|
return Promise.resolve(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigManager.prototype.markNoticeRead = function (notice) {
|
||||||
|
var notices = this.getNoticesList()
|
||||||
|
var id = notice.id
|
||||||
|
notices[id].read = true
|
||||||
|
this.setNoticesList(notices)
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigManager.prototype.updateNoticesList = function () {
|
||||||
|
return this._retrieveNoticeData().then((newNotices) => {
|
||||||
|
var oldNotices = this.getNoticesList()
|
||||||
|
var combinedNotices = this._mergeNotices(oldNotices, newNotices)
|
||||||
|
return Promise.resolve(this.setNoticesList(combinedNotices))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigManager.prototype.getLatestUnreadNotice = function () {
|
||||||
|
var notices = this.getNoticesList()
|
||||||
|
var filteredNotices = notices.filter((notice) => {
|
||||||
|
return notice.read === false
|
||||||
|
})
|
||||||
|
return filteredNotices[filteredNotices.length - 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigManager.prototype._mergeNotices = function (oldNotices, newNotices) {
|
||||||
|
var noticeMap = this._mapNoticeIds(oldNotices)
|
||||||
|
newNotices.forEach((notice) => {
|
||||||
|
if (noticeMap.indexOf(notice.id) === -1) {
|
||||||
|
oldNotices.push(notice)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return oldNotices
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigManager.prototype._mapNoticeIds = function (notices) {
|
||||||
|
return notices.map((notice) => notice.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigManager.prototype._retrieveNoticeData = function () {
|
||||||
|
// Placeholder for the API.
|
||||||
|
return Promise.resolve(notices)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Tx
|
// Tx
|
||||||
//
|
//
|
||||||
|
@ -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,9 @@ module.exports = class MetamaskController {
|
|||||||
this.idStore = new IdentityStore({
|
this.idStore = new IdentityStore({
|
||||||
configManager: this.configManager,
|
configManager: this.configManager,
|
||||||
})
|
})
|
||||||
|
this.noticeController = new NoticeController({
|
||||||
|
configManager: this.configManager,
|
||||||
|
})
|
||||||
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)
|
||||||
@ -27,17 +31,19 @@ module.exports = class MetamaskController {
|
|||||||
this.configManager.setCurrentFiat(currentFiat)
|
this.configManager.setCurrentFiat(currentFiat)
|
||||||
this.configManager.updateConversionRate()
|
this.configManager.updateConversionRate()
|
||||||
|
|
||||||
|
this.checkNotices()
|
||||||
this.checkTOSChange()
|
this.checkTOSChange()
|
||||||
|
|
||||||
this.scheduleConversionInterval()
|
this.scheduleConversionInterval()
|
||||||
|
this.scheduleNoticeCheck()
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,6 +61,7 @@ module.exports = class MetamaskController {
|
|||||||
agreeToEthWarning: this.agreeToEthWarning.bind(this),
|
agreeToEthWarning: this.agreeToEthWarning.bind(this),
|
||||||
setTOSHash: this.setTOSHash.bind(this),
|
setTOSHash: this.setTOSHash.bind(this),
|
||||||
checkTOSChange: this.checkTOSChange.bind(this),
|
checkTOSChange: this.checkTOSChange.bind(this),
|
||||||
|
checkNotices: this.checkNotices.bind(this),
|
||||||
setGasMultiplier: this.setGasMultiplier.bind(this),
|
setGasMultiplier: this.setGasMultiplier.bind(this),
|
||||||
|
|
||||||
// forward directly to idStore
|
// forward directly to idStore
|
||||||
@ -77,6 +84,8 @@ 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
|
||||||
|
markNoticeRead: this.markNoticeRead.bind(this),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,6 +298,25 @@ module.exports = class MetamaskController {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
checkNotices () {
|
||||||
|
try {
|
||||||
|
this.configManager.updateNoticesList()
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error in checking notices.')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// notice
|
||||||
|
|
||||||
|
markNoticeRead (notice, cb) {
|
||||||
|
try {
|
||||||
|
this.configManager.markNoticeRead(notice)
|
||||||
|
cb(null, this.configManager.getLatestUnreadNotice())
|
||||||
|
} catch (e) {
|
||||||
|
cb(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
agreeToDisclaimer (cb) {
|
agreeToDisclaimer (cb) {
|
||||||
try {
|
try {
|
||||||
this.configManager.setConfirmed(true)
|
this.configManager.setConfirmed(true)
|
||||||
@ -331,6 +359,7 @@ module.exports = class MetamaskController {
|
|||||||
}, 300000)
|
}, 300000)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
agreeToEthWarning (cb) {
|
agreeToEthWarning (cb) {
|
||||||
try {
|
try {
|
||||||
this.configManager.setShouldntShowWarning()
|
this.configManager.setShouldntShowWarning()
|
||||||
@ -338,6 +367,15 @@ module.exports = class MetamaskController {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
cb(e)
|
cb(e)
|
||||||
}
|
}
|
||||||
|
=======
|
||||||
|
scheduleNoticeCheck () {
|
||||||
|
if (this.noticeCheck) {
|
||||||
|
clearInterval(this.noticeCheck)
|
||||||
|
}
|
||||||
|
this.noticeCheck = setInterval(() => {
|
||||||
|
this.configManager.updateNoticesList()
|
||||||
|
}, 300000)
|
||||||
|
>>>>>>> 25acad7... Add ability to show notices to user & get confirmation.
|
||||||
}
|
}
|
||||||
|
|
||||||
// called from popup
|
// called from popup
|
||||||
|
18
app/scripts/notice-controller.js
Normal file
18
app/scripts/notice-controller.js
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
const EventEmitter = require('events').EventEmitter
|
||||||
|
|
||||||
|
module.exports = class NoticeController extends EventEmitter {
|
||||||
|
|
||||||
|
constructor (opts) {
|
||||||
|
super()
|
||||||
|
this.configManager = opts.configManager
|
||||||
|
}
|
||||||
|
|
||||||
|
getState() {
|
||||||
|
var lastUnreadNotice = this.configManager.getLatestUnreadNotice()
|
||||||
|
|
||||||
|
return {
|
||||||
|
lastUnreadNotice: lastUnreadNotice,
|
||||||
|
noActiveNotices: !lastUnreadNotice,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
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":"Tue Dec 13 2016","title":"MultiVault Support","body":"# Multi\n# Line\n## Support\n\n### MultiVault declaration\n\nThe winds of change are coming.\n","id":0},{"read":false,"date":"Tue Dec 13 2016","title":"Rocket League!","body":"MetaMask development is now purely going eSports, as we convert our team into a professional Rocket League team.\n\nWe are now hiring for a fifth player! Please submit your CV. Must be proficient in both eSports and JavaScript debugging.\n","id":1},{"read":false,"date":"Thu Dec 15 2016","title":"Link Test","body":"This is a cool notice with a [link](https://metamask.io)!","id":2}]
|
63
development/states/notice.json
Normal file
63
development/states/notice.json
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
{
|
||||||
|
"metamask": {
|
||||||
|
"isInitialized": true,
|
||||||
|
"isUnlocked": true,
|
||||||
|
"rpcTarget": "https://rawtestrpc.metamask.io/",
|
||||||
|
"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": {}
|
||||||
|
}
|
@ -16,7 +16,8 @@
|
|||||||
"buildMock": "browserify ./mock-dev.js -o ./development/bundle.js",
|
"buildMock": "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 +48,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 +100,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 +120,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",
|
||||||
|
@ -3,6 +3,7 @@ const extend = require('xtend')
|
|||||||
const STORAGE_KEY = 'metamask-persistance-key'
|
const STORAGE_KEY = 'metamask-persistance-key'
|
||||||
var configManagerGen = require('../lib/mock-config-manager')
|
var configManagerGen = require('../lib/mock-config-manager')
|
||||||
var configManager
|
var configManager
|
||||||
|
var testList
|
||||||
const rp = require('request-promise')
|
const rp = require('request-promise')
|
||||||
const nock = require('nock')
|
const nock = require('nock')
|
||||||
|
|
||||||
@ -13,6 +14,100 @@ describe('config-manager', function() {
|
|||||||
configManager = configManagerGen()
|
configManager = configManagerGen()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
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 = configManager.getNoticesList()
|
||||||
|
assert.equal(result.length, 0)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('#setNoticesList', function() {
|
||||||
|
it('should set data appropriately', function () {
|
||||||
|
var testList = [{
|
||||||
|
id:0,
|
||||||
|
read:false,
|
||||||
|
title:"Futuristic Notice"
|
||||||
|
}]
|
||||||
|
configManager.setNoticesList(testList)
|
||||||
|
var testListId = configManager.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"
|
||||||
|
}]
|
||||||
|
configManager.setNoticesList(testList)
|
||||||
|
configManager.updateNoticesList().then(() => {
|
||||||
|
var newList = configManager.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"
|
||||||
|
}]
|
||||||
|
configManager.setNoticesList(testList)
|
||||||
|
configManager.updateNoticesList().then(() => {
|
||||||
|
var newList = configManager.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"
|
||||||
|
}]
|
||||||
|
configManager.setNoticesList(testList)
|
||||||
|
configManager.markNoticeRead(testList[0])
|
||||||
|
var newList = configManager.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"},
|
||||||
|
]
|
||||||
|
configManager.setNoticesList(testList)
|
||||||
|
var latestUnread = configManager.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"},
|
||||||
|
]
|
||||||
|
configManager.setNoticesList(testList)
|
||||||
|
var latestUnread = configManager.getLatestUnreadNotice()
|
||||||
|
assert.ok(!latestUnread)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe('currency conversions', function() {
|
describe('currency conversions', function() {
|
||||||
|
|
||||||
describe('#getCurrentFiat', function() {
|
describe('#getCurrentFiat', function() {
|
||||||
|
@ -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())
|
||||||
|
background.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)
|
||||||
|
}
|
||||||
|
105
ui/app/notice.js
Normal file
105
ui/app/notice.js
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
return (
|
||||||
|
h('.flex-column.flex-center.flex-grow', [
|
||||||
|
h('h3.flex-center.text-transform-uppercacse.terms-header', {
|
||||||
|
style: {
|
||||||
|
background: '#EBEBEB',
|
||||||
|
color: '#AEAEAE',
|
||||||
|
marginBottom: 24,
|
||||||
|
width: '100%',
|
||||||
|
fontSize: '20px',
|
||||||
|
textAlign: 'center',
|
||||||
|
padding: 6,
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
title,
|
||||||
|
]),
|
||||||
|
|
||||||
|
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: blue;
|
||||||
|
}
|
||||||
|
|
||||||
|
`),
|
||||||
|
|
||||||
|
h('div.markdown', {
|
||||||
|
style: {
|
||||||
|
background: 'rgb(235, 235, 235)',
|
||||||
|
height: '310px',
|
||||||
|
padding: '6px',
|
||||||
|
width: '90%',
|
||||||
|
overflowY: 'scroll',
|
||||||
|
scroll: 'auto',
|
||||||
|
},
|
||||||
|
}, [
|
||||||
|
`${props.lastUnreadNotice.title}`,
|
||||||
|
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