Bytom Wallet for Chrome
Revision | 1c5f87e0cb7dc0cf1aea8481f4ce18997a47c413 (tree) |
---|---|
Time | 2019-06-25 10:53:58 |
Author | Zhiting Lin <zlin035@uott...> |
Commiter | Zhiting Lin |
update enable function for bytom
@@ -51,6 +51,13 @@ const cn = { | ||
51 | 51 | message:'签名消息', |
52 | 52 | confirmSignature:'确认签名' |
53 | 53 | }, |
54 | + enable:{ | |
55 | + title:'请求授权', | |
56 | + domain: '域名', | |
57 | + message: '请求获取你的钱包地址,是否同意?', | |
58 | + cancel:'取消', | |
59 | + confirm:'确认授权' | |
60 | + }, | |
54 | 61 | receive:{ |
55 | 62 | address: '地址', |
56 | 63 | tips:'提示:点击地址进行拷贝。' |
@@ -51,6 +51,13 @@ const en = { | ||
51 | 51 | message:'Sign Message', |
52 | 52 | confirmSignature:'Sign' |
53 | 53 | }, |
54 | + enable:{ | |
55 | + title:'Connect Request', | |
56 | + domain: 'Domain', | |
57 | + message: 'would like to connect your account', | |
58 | + cancel:'Cancel', | |
59 | + confirm:'Connect' | |
60 | + }, | |
54 | 61 | receive:{ |
55 | 62 | address: 'Address', |
56 | 63 | tips:'Tips: Click address to copy directly.' |
@@ -1,11 +1,17 @@ | ||
1 | 1 | import { LocalStream } from 'extension-streams' |
2 | 2 | import InternalMessage from '@/messages/internal' |
3 | 3 | import * as MsgTypes from './messages/types' |
4 | +import NotificationService from './services/NotificationService' | |
5 | +import StorageService from './services/StorageService' | |
6 | +import Prompt from './prompts/Prompt'; | |
7 | +import * as PromptTypes from './prompts/PromptTypes' | |
4 | 8 | |
5 | 9 | import Error from './utils/errors/Error' |
6 | 10 | import accountAction from "@/models/account"; |
7 | 11 | import bytom from "@/models/bytom"; |
8 | 12 | |
13 | +let prompt = null; | |
14 | + | |
9 | 15 | export default class Background { |
10 | 16 | constructor() { |
11 | 17 | this.setupInternalMessaging() |
@@ -54,17 +60,32 @@ export default class Background { | ||
54 | 60 | this.signMessage(sendResponse, message.payload) |
55 | 61 | break |
56 | 62 | case MsgTypes.REQUEST_CURRENT_ACCOUNT: |
57 | - this.requestCurrentAccount(sendResponse) | |
63 | + this.requestCurrentAccount(sendResponse, message.payload) | |
58 | 64 | break |
59 | 65 | case MsgTypes.REQUEST_CURRENT_NETWORK: |
60 | 66 | this.requestCurrentNetwork(sendResponse) |
61 | 67 | break |
62 | - case MsgTypes.REQUEST_ACCOUNT_LIST: | |
63 | - this.requestAccountList(sendResponse) | |
68 | + case MsgTypes.ENABLE: | |
69 | + Background.authenticate(sendResponse, message.payload) | |
64 | 70 | break |
71 | + case MsgTypes.SET_PROMPT: | |
72 | + Background.setPrompt(sendResponse, message.payload); | |
73 | + break; | |
74 | + case MsgTypes.GET_PROMPT: | |
75 | + Background.getPrompt(sendResponse); | |
76 | + break; | |
65 | 77 | } |
66 | 78 | } |
67 | 79 | |
80 | + static setPrompt(sendResponse, notification){ | |
81 | + prompt = notification; | |
82 | + sendResponse(true); | |
83 | + } | |
84 | + | |
85 | + static getPrompt(sendResponse){ | |
86 | + sendResponse(prompt); | |
87 | + } | |
88 | + | |
68 | 89 | signMessage(sendResponse, payload) { |
69 | 90 | var promptURL = chrome.extension.getURL('pages/prompt.html') |
70 | 91 | var requestBody = payload |
@@ -105,12 +126,12 @@ export default class Background { | ||
105 | 126 | } |
106 | 127 | }); |
107 | 128 | |
108 | - chrome.windows.onRemoved.addListener(function(windowId){ | |
109 | - if(windowId === window.id) { | |
110 | - sendResponse(Error.promptClosedWithoutAction()); | |
111 | - return false; | |
112 | - } | |
113 | - }); | |
129 | + // chrome.windows.onRemoved.addListener(function(windowId){ | |
130 | + // if(windowId === window.id) { | |
131 | + // sendResponse(Error.promptClosedWithoutAction()); | |
132 | + // return false; | |
133 | + // } | |
134 | + // }); | |
114 | 135 | } |
115 | 136 | ) |
116 | 137 | } |
@@ -227,35 +248,30 @@ export default class Background { | ||
227 | 248 | ) |
228 | 249 | } |
229 | 250 | |
230 | - requestCurrentAccount(sendResponse){ | |
231 | - const currentAccount = JSON.parse(localStorage.currentAccount) | |
232 | - delete(currentAccount['label']) | |
233 | - delete(currentAccount['net']) | |
234 | - currentAccount['accountId'] = currentAccount['guid'] | |
235 | - delete(currentAccount['guid']) | |
236 | - delete(currentAccount['balance']) | |
251 | + requestCurrentAccount(sendResponse, payload){ | |
252 | + Background.load(bytom => { | |
253 | + const domain = payload.domain; | |
254 | + if(bytom.settings.domains.find(_domain => _domain === domain)) { | |
255 | + const currentAccount = JSON.parse(localStorage.currentAccount) | |
256 | + delete(currentAccount['label']) | |
257 | + delete(currentAccount['net']) | |
258 | + currentAccount['accountId'] = currentAccount['guid'] | |
259 | + delete(currentAccount['guid']) | |
260 | + delete(currentAccount['balance']) | |
261 | + | |
262 | + sendResponse(currentAccount); | |
263 | + } else{ | |
264 | + sendResponse(null); | |
265 | + return false; | |
266 | + } | |
267 | + }) | |
237 | 268 | |
238 | - sendResponse(currentAccount) | |
239 | 269 | } |
240 | 270 | |
241 | 271 | requestCurrentNetwork(sendResponse){ |
242 | 272 | sendResponse(localStorage.bytomNet) |
243 | 273 | } |
244 | 274 | |
245 | - requestAccountList(sendResponse){ | |
246 | - accountAction.list().then(resp=>{ | |
247 | - const accountList = resp | |
248 | - accountList.forEach(function(account) { | |
249 | - delete(account['label']) | |
250 | - delete(account['net']) | |
251 | - account['accountId'] = account['guid'] | |
252 | - delete(account['guid']) | |
253 | - delete(account['balance']) | |
254 | - }) | |
255 | - sendResponse(accountList) | |
256 | - }) | |
257 | - } | |
258 | - | |
259 | 275 | send(sendResponse, payload) { |
260 | 276 | const action = payload.action |
261 | 277 | if(action){ |
@@ -273,6 +289,59 @@ export default class Background { | ||
273 | 289 | } |
274 | 290 | } |
275 | 291 | } |
292 | + | |
293 | + /*** | |
294 | + * Returns the saved instance of Bytom from the storage | |
295 | + * @param sendResponse - Delegating response handler | |
296 | + * @returns {Bytom} | |
297 | + */ | |
298 | + static load(sendResponse){ | |
299 | + StorageService.get().then(bytom => { | |
300 | + sendResponse(bytom) | |
301 | + }) | |
302 | + } | |
303 | + | |
304 | + /*** | |
305 | + * Updates the Scatter instance inside persistent storage | |
306 | + * @param sendResponse - Delegating response handler | |
307 | + * @param bytom - The updated cleartext Scatter instance | |
308 | + * @returns {boolean} | |
309 | + */ | |
310 | + static update(sendResponse, bytom){ | |
311 | + StorageService.save(bytom).then(saved => { | |
312 | + sendResponse(bytom) | |
313 | + }) | |
314 | + } | |
315 | + | |
316 | + static authenticate(sendResponse, payload){ | |
317 | + Background.load(bytom => { | |
318 | + const domain = payload.domain; | |
319 | + const currentAccount = JSON.parse(localStorage.currentAccount) | |
320 | + delete(currentAccount['label']) | |
321 | + delete(currentAccount['net']) | |
322 | + currentAccount['accountId'] = currentAccount['guid'] | |
323 | + delete(currentAccount['guid']) | |
324 | + delete(currentAccount['balance']) | |
325 | + | |
326 | + if(bytom.settings.domains.find(_domain => _domain === domain)) { | |
327 | + sendResponse(currentAccount); | |
328 | + } else{ | |
329 | + NotificationService.open(new Prompt(PromptTypes.REQUEST_AUTH, payload.domain, approved => { | |
330 | + if(approved === false || approved.hasOwnProperty('isError')) sendResponse(approved); | |
331 | + else { | |
332 | + bytom.settings.domains.unshift(domain); | |
333 | + if(approved === true){ | |
334 | + this.update(() => sendResponse(currentAccount), bytom); | |
335 | + }else{ | |
336 | + this.update(() => sendResponse(approved), bytom); | |
337 | + } | |
338 | + } | |
339 | + })); | |
340 | + } | |
341 | + }) | |
342 | + } | |
343 | + | |
344 | + | |
276 | 345 | } |
277 | 346 | |
278 | 347 | new Background() |
@@ -4,6 +4,8 @@ import NetworkMessage from '@/messages/network' | ||
4 | 4 | import InternalMessage from '@/messages/internal' |
5 | 5 | import * as MsgTypes from './messages/types' |
6 | 6 | import * as EventNames from '@/messages/event' |
7 | +import {strippedHost} from '@/utils/GenericTools' | |
8 | + | |
7 | 9 | |
8 | 10 | let stream = new WeakMap() |
9 | 11 | let INJECTION_SCRIPT_FILENAME = 'js/inject.js' |
@@ -31,11 +33,10 @@ class Content { | ||
31 | 33 | stream.onSync(async () => { |
32 | 34 | const defaultAccount = await this.getDefaultAccount(); |
33 | 35 | const net = await this.getDefaultNetwork(); |
34 | - const accountList = await this.getAccountList(); | |
35 | 36 | |
36 | 37 | // Pushing an instance of Bytomdapp to the web application |
37 | 38 | stream.send( |
38 | - NetworkMessage.payload(MsgTypes.PUSH_BYTOM, {defaultAccount, net, accountList}), | |
39 | + NetworkMessage.payload(MsgTypes.PUSH_BYTOM, {defaultAccount, net}), | |
39 | 40 | EventNames.INJECT |
40 | 41 | ) |
41 | 42 |
@@ -95,6 +96,9 @@ class Content { | ||
95 | 96 | case MsgTypes.SEND: |
96 | 97 | this.transfer(msg.type, networkMessage) |
97 | 98 | break |
99 | + case MsgTypes.ENABLE: | |
100 | + this.enable(msg.type, networkMessage) | |
101 | + break | |
98 | 102 | default: |
99 | 103 | stream.send(networkMessage.error('errtest'), EventNames.INJECT) |
100 | 104 | break |
@@ -104,7 +108,7 @@ class Content { | ||
104 | 108 | getVersion() {} |
105 | 109 | |
106 | 110 | getDefaultAccount(){ |
107 | - return InternalMessage.signal(MsgTypes.REQUEST_CURRENT_ACCOUNT) | |
111 | + return InternalMessage.payload(MsgTypes.REQUEST_CURRENT_ACCOUNT,{domain:strippedHost()}) | |
108 | 112 | .send() |
109 | 113 | } |
110 | 114 |
@@ -113,11 +117,6 @@ class Content { | ||
113 | 117 | .send() |
114 | 118 | } |
115 | 119 | |
116 | - getAccountList(){ | |
117 | - return InternalMessage.signal(MsgTypes.REQUEST_ACCOUNT_LIST) | |
118 | - .send() | |
119 | - } | |
120 | - | |
121 | 120 | respond(message, payload) { |
122 | 121 | if (!isReady) return |
123 | 122 |
@@ -142,6 +141,15 @@ class Content { | ||
142 | 141 | .send() |
143 | 142 | .then(res => this.respond(message, res)) |
144 | 143 | } |
144 | + | |
145 | + enable(type, networkMessage) { | |
146 | + networkMessage.payload ={ | |
147 | + domain: strippedHost() | |
148 | + } | |
149 | + | |
150 | + this.transfer(type, networkMessage) | |
151 | + } | |
152 | + | |
145 | 153 | } |
146 | 154 | |
147 | 155 | const content = new Content() |
@@ -66,6 +66,14 @@ export default class Bytomdapp { | ||
66 | 66 | _subscribe() |
67 | 67 | } |
68 | 68 | |
69 | + enable(){ | |
70 | + return _send(MsgTypes.ENABLE) | |
71 | + .then(async default_account =>{ | |
72 | + this.default_account = default_account; | |
73 | + return default_account; | |
74 | + }) | |
75 | + } | |
76 | + | |
69 | 77 | send_transaction(params) { |
70 | 78 | return _send(MsgTypes.TRANSFER, params) |
71 | 79 | } |
@@ -4,6 +4,7 @@ export const PUSH_BYTOM = 'pushBytom' | ||
4 | 4 | export const UPDATE_BYTOM = 'updateBytom' |
5 | 5 | export const AUTHENTICATE = 'authenticate' |
6 | 6 | export const TRANSFER = 'transfer' |
7 | +export const ENABLE = 'enable' | |
7 | 8 | export const ADVTRANSFER = 'advTransfer' |
8 | 9 | export const SIGNMESSAGE = 'signMessage' |
9 | 10 | export const SEND = 'send' |
@@ -12,3 +13,8 @@ export const SEND = 'send' | ||
12 | 13 | export const REQUEST_CURRENT_ACCOUNT = 'defaultAccount'; |
13 | 14 | export const REQUEST_CURRENT_NETWORK = 'currentNetwork'; |
14 | 15 | export const REQUEST_ACCOUNT_LIST = 'accountList'; |
16 | + | |
17 | + | |
18 | +//Internal Message | |
19 | +export const SET_PROMPT = 'setPrompt'; | |
20 | +export const GET_PROMPT = 'getPrompt'; |
@@ -0,0 +1,18 @@ | ||
1 | +export default class Prompt { | |
2 | + | |
3 | + constructor(_type = '', _domain = '', _responder = null){ | |
4 | + this.type = _type; | |
5 | + this.domain = _domain; | |
6 | + // this.network = _network; | |
7 | + // this.data = _data; | |
8 | + this.responder = _responder; | |
9 | + } | |
10 | + | |
11 | + static placeholder(){ return new Prompt(); } | |
12 | + static fromJson(json){ return Object.assign(this.placeholder(), json); } | |
13 | + | |
14 | + routeName(){ | |
15 | + return `${this.type}`; | |
16 | + } | |
17 | + | |
18 | +} |
@@ -0,0 +1,6 @@ | ||
1 | +export const REQUEST_AUTH = 'enable'; | |
2 | +export const REQUEST_SIGNATURE = 'requestSignature'; | |
3 | +export const REQUEST_ARBITRARY_SIGNATURE = 'signatureArbitrary'; | |
4 | +export const REQUEST_ADD_NETWORK = 'requestAddNetwork'; | |
5 | +export const REQUEST_UNLOCK = 'requestUnlock'; | |
6 | +export const UPDATE_VERSION = 'updateVersion'; |
@@ -42,6 +42,14 @@ const routers = [ | ||
42 | 42 | } |
43 | 43 | }, |
44 | 44 | { |
45 | + path: '/enable', | |
46 | + name: 'enable', | |
47 | + meta: { title: '授权' }, | |
48 | + component: resolve => { | |
49 | + require(['@/views/prompts/authentication.vue'], resolve) | |
50 | + } | |
51 | + }, | |
52 | + { | |
45 | 53 | path: '/transfer/info', |
46 | 54 | name: 'transfer-info', |
47 | 55 | meta: { title: '交易详情' }, |
@@ -0,0 +1,103 @@ | ||
1 | +import Error from '../utils/errors/Error' | |
2 | +import {apis} from '../utils/BrowserApis'; | |
3 | +import InternalMessage from '../messages/internal' | |
4 | +import * as InternalMessageTypes from '../messages/types' | |
5 | + | |
6 | +let openWindow = null; | |
7 | + | |
8 | +export default class NotificationService { | |
9 | + | |
10 | + /*** | |
11 | + * Opens a prompt window outside of the extension | |
12 | + * @param notification | |
13 | + */ | |
14 | + static async open(notification){ | |
15 | + if(openWindow){ | |
16 | + // For now we're just going to close the window to get rid of the error | |
17 | + // that is caused by already open windows swallowing all further requests | |
18 | + openWindow.close(); | |
19 | + openWindow = null; | |
20 | + | |
21 | + // Alternatively we could focus the old window, but this would cause | |
22 | + // urgent 1-time messages to be lost, such as after dying in a game and | |
23 | + // uploading a high-score. That message will be lost. | |
24 | + // openWindow.focus(); | |
25 | + // return false; | |
26 | + | |
27 | + // A third option would be to add a queue, but this could cause | |
28 | + // virus-like behavior as apps overflow the queue causing the user | |
29 | + // to have to quit the browser to regain control. | |
30 | + } | |
31 | + | |
32 | + | |
33 | + const height = 623; | |
34 | + const width = 360; | |
35 | + let middleX = window.screen.availWidth/2 - (width/2); | |
36 | + let middleY = window.screen.availHeight/2 - (height/2); | |
37 | + | |
38 | + const getPopup = async () => { | |
39 | + try { | |
40 | + const url = `${apis.runtime.getURL('pages/prompt.html')}#${notification.routeName()}`; | |
41 | + | |
42 | + // Notifications get bound differently depending on browser | |
43 | + // as Firefox does not support opening windows from background. | |
44 | + if(typeof chrome !== 'undefined') { | |
45 | + window.notification = notification; | |
46 | + apis.windows.create({ | |
47 | + url, | |
48 | + height, | |
49 | + width, | |
50 | + type:'popup' | |
51 | + },(_window) => { | |
52 | + apis.windows.onRemoved.addListener(function(windowId){ | |
53 | + if(windowId === _window.id) { | |
54 | + notification.responder(Error.promptClosedWithoutAction()); | |
55 | + return false; | |
56 | + } | |
57 | + }); | |
58 | + return _window; | |
59 | + }); | |
60 | + } | |
61 | + else { | |
62 | + const win = window.open(url, 'BytomPrompt', `width=${width},height=${height},resizable=0,top=${middleY},left=${middleX},titlebar=0`); | |
63 | + win.data = notification; | |
64 | + openWindow = win; | |
65 | + return win; | |
66 | + } | |
67 | + } catch (e) { | |
68 | + console.log('notification error', e); | |
69 | + return null; | |
70 | + } | |
71 | + } | |
72 | + | |
73 | + await InternalMessage.payload(InternalMessageTypes.SET_PROMPT, JSON.stringify(notification)).send(); | |
74 | + | |
75 | + let popup = await getPopup(); | |
76 | + | |
77 | + if(popup){ | |
78 | + popup.onbeforeunload = () => { | |
79 | + notification.responder(Error.promptClosedWithoutAction()); | |
80 | + | |
81 | + // https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onbeforeunload | |
82 | + // Must return undefined to bypass form protection | |
83 | + openWindow = null; | |
84 | + return undefined; | |
85 | + }; | |
86 | + } | |
87 | + } | |
88 | + | |
89 | + /*** | |
90 | + * Always use this method for closing notification popups. | |
91 | + * Otherwise you will double send responses and one will always be null. | |
92 | + */ | |
93 | + static async close(){ | |
94 | + if(typeof browser !== 'undefined') { | |
95 | + const {id: windowId,} = (await apis.windows.getCurrent()); | |
96 | + apis.windows.remove(windowId); | |
97 | + } else { | |
98 | + window.onbeforeunload = () => {}; | |
99 | + window.close(); | |
100 | + } | |
101 | + } | |
102 | + | |
103 | +} |
@@ -0,0 +1,97 @@ | ||
1 | +import BytomObj from '../utils/Bytom' | |
2 | +import {apis} from '../utils/BrowserApis'; | |
3 | + | |
4 | +export default class StorageService { | |
5 | + | |
6 | + constructor(){} | |
7 | + | |
8 | + /*** | |
9 | + * Saves an instance of Bytom in the extension's local storage | |
10 | + * The keychain will always be encrypted when in storage | |
11 | + * @param bytom | |
12 | + * @returns {Promise} | |
13 | + */ | |
14 | + static save(_bytom){ | |
15 | + return new Promise(resolve => { | |
16 | + apis.storage.local.set({bytom:_bytom}, () => { | |
17 | + resolve(_bytom); | |
18 | + }); | |
19 | + }) | |
20 | + }; | |
21 | + | |
22 | + /*** | |
23 | + * Gets an instance of Bytom from the extension's local storage | |
24 | + * Will return a new Bytom instance if not found. | |
25 | + * @returns {Promise} | |
26 | + */ | |
27 | + static get() { | |
28 | + return new Promise(resolve => { | |
29 | + apis.storage.local.get('bytom', (possible) => { | |
30 | + (possible && Object.keys(possible).length && possible.hasOwnProperty('bytom')) | |
31 | + ? resolve(BytomObj.fromJson(possible.bytom)) | |
32 | + : resolve(BytomObj.placeholder()); | |
33 | + }); | |
34 | + }) | |
35 | + } | |
36 | + | |
37 | + /*** | |
38 | + * Removes the instance of Bytom. | |
39 | + * This will delete all user data. | |
40 | + * @returns {Promise} | |
41 | + */ | |
42 | + static remove(){ | |
43 | + return new Promise(resolve => { | |
44 | + apis.storage.local.remove('bytom', () => { | |
45 | + resolve(); | |
46 | + }); | |
47 | + }) | |
48 | + } | |
49 | + | |
50 | + /*** | |
51 | + * Caches an ABI | |
52 | + * @param contractName | |
53 | + * @param chainId | |
54 | + * @param abi | |
55 | + * @returns {Promise} | |
56 | + */ | |
57 | + static cacheABI(contractName, chainId, abi){ | |
58 | + return new Promise(resolve => { | |
59 | + apis.storage.local.set({[`abi:${contractName}:${chainId}`]:abi}, () => { | |
60 | + resolve(abi); | |
61 | + }); | |
62 | + }); | |
63 | + } | |
64 | + | |
65 | + /*** | |
66 | + * Fetches an ABI from cache | |
67 | + * @param contractName | |
68 | + * @param chainId | |
69 | + * @returns {Promise} | |
70 | + */ | |
71 | + static getABI(contractName, chainId){ | |
72 | + return new Promise(resolve => { | |
73 | + const prop = `abi:${contractName}:${chainId}`; | |
74 | + apis.storage.local.get(prop, (possible) => { | |
75 | + if(JSON.stringify(possible) !== '{}') resolve(possible[prop]); | |
76 | + else resolve('no cache'); | |
77 | + }); | |
78 | + }) | |
79 | + } | |
80 | + | |
81 | + static getSalt(){ | |
82 | + return new Promise(resolve => { | |
83 | + apis.storage.local.get('salt', (possible) => { | |
84 | + if(JSON.stringify(possible) !== '{}') resolve(possible.salt); | |
85 | + else resolve('SALT_ME'); | |
86 | + }); | |
87 | + }) | |
88 | + } | |
89 | + | |
90 | + static setSalt(salt){ | |
91 | + return new Promise(resolve => { | |
92 | + apis.storage.local.set({salt}, () => { | |
93 | + resolve(salt); | |
94 | + }); | |
95 | + }) | |
96 | + } | |
97 | +} |
@@ -0,0 +1,22 @@ | ||
1 | + | |
2 | +const swallow = fn => {try { fn() } catch(e){}}; | |
3 | + | |
4 | +class ApiGenerator { | |
5 | + constructor(){ | |
6 | + [ | |
7 | + 'app', | |
8 | + 'storage', | |
9 | + 'extension', | |
10 | + 'runtime', | |
11 | + 'windows' | |
12 | + ] | |
13 | + .map(api => { | |
14 | + if(typeof chrome !== 'undefined') swallow(() => {if(chrome[api]) this[api] = chrome[api]}); | |
15 | + if(typeof browser !== 'undefined') swallow(() => {if(browser[api]) this[api] = browser[api]}); | |
16 | + }); | |
17 | + | |
18 | + if(typeof browser !== 'undefined') swallow(() => {if (browser && browser.runtime) this.runtime = browser.runtime}); | |
19 | + } | |
20 | +} | |
21 | + | |
22 | +export const apis = new ApiGenerator(); | |
\ No newline at end of file |
@@ -0,0 +1,17 @@ | ||
1 | +import Settings from './Settings'; | |
2 | + | |
3 | +export default class BytomObj { | |
4 | + | |
5 | + constructor(){ | |
6 | + this.settings = Settings.placeholder(); | |
7 | + } | |
8 | + | |
9 | + static placeholder(){ return new BytomObj(); } | |
10 | + static fromJson(json){ | |
11 | + let p = Object.assign(this.placeholder(), json); | |
12 | + if(json.hasOwnProperty('settings')) p.settings = Settings.fromJson(json.settings); | |
13 | + return p; | |
14 | + } | |
15 | + | |
16 | + clone(){ return BytomObj.fromJson(JSON.parse(JSON.stringify(this))) } | |
17 | +} |
@@ -0,0 +1,9 @@ | ||
1 | + | |
2 | +export const strippedHost = () => { | |
3 | + let host = location.hostname; | |
4 | + | |
5 | + // Replacing www. only if the domain starts with it. | |
6 | + if(host.indexOf('www.') === 0) host = host.replace('www.', ''); | |
7 | + | |
8 | + return host; | |
9 | +}; | |
\ No newline at end of file |
@@ -0,0 +1,14 @@ | ||
1 | +export default class Settings { | |
2 | + | |
3 | + constructor(){ | |
4 | + this.domains = []; | |
5 | + this.language = 'ENGLISH'; | |
6 | + } | |
7 | + | |
8 | + static placeholder(){ return new Settings(); } | |
9 | + static fromJson(json){ | |
10 | + let p = Object.assign(this.placeholder(), json); | |
11 | + if(json.hasOwnProperty('domains')) p.domains = json.domains; | |
12 | + return p; | |
13 | + } | |
14 | +} |
@@ -19,7 +19,7 @@ export default class Error { | ||
19 | 19 | } |
20 | 20 | |
21 | 21 | static locked(){ |
22 | - return new Error(ErrorTypes.LOCKED, "The user's Scatter is locked. They have been notified and should unlock before continuing.") | |
22 | + return new Error(ErrorTypes.LOCKED, "The user's Bytom is locked. They have been notified and should unlock before continuing.") | |
23 | 23 | } |
24 | 24 | |
25 | 25 | static promptClosedWithoutAction(){ |
@@ -49,7 +49,7 @@ export default class Error { | ||
49 | 49 | static usedKeyProvider(){ |
50 | 50 | return new Error( |
51 | 51 | ErrorTypes.MALICIOUS, |
52 | - "Do not use a `keyProvider` with a Scatter. Use a `signProvider` and return only signatures to this object. A malicious person could retrieve your keys otherwise.", | |
52 | + "Do not use a `keyProvider` with a Bytom. Use a `signProvider` and return only signatures to this object. A malicious person could retrieve your keys otherwise.", | |
53 | 53 | ErrorCodes.NO_SIGNATURE |
54 | 54 | ) |
55 | 55 | } |
@@ -0,0 +1,124 @@ | ||
1 | +<style lang="" scoped> | |
2 | + .warp { | |
3 | + position: absolute; | |
4 | + top: 0; | |
5 | + left: 0; | |
6 | + right: 0; | |
7 | + height: 600px; | |
8 | + z-index: 2; | |
9 | + overflow: scroll; | |
10 | + } | |
11 | + .header { | |
12 | + display: flex; | |
13 | + } | |
14 | + .header p{ | |
15 | + text-align: center; | |
16 | + width: 280px; | |
17 | + padding-top: 17px; | |
18 | + } | |
19 | + | |
20 | + .content { | |
21 | + margin: 20px; | |
22 | + padding: 20px; | |
23 | + overflow: hidden; | |
24 | + border-radius:4px; | |
25 | + width: 280px; | |
26 | + } | |
27 | + | |
28 | + .btn-inline .btn { | |
29 | + margin: 10px 15px; | |
30 | + } | |
31 | + .row{ | |
32 | + word-break: break-all; | |
33 | + } | |
34 | + .col{ | |
35 | + font-size: 14px; | |
36 | + width: 35%; | |
37 | + } | |
38 | + .label{ | |
39 | + color: #7B7B7B; | |
40 | + } | |
41 | + .message{ | |
42 | + font-size: 14px; | |
43 | + color: #7B7B7B; | |
44 | + } | |
45 | + .value{ | |
46 | + color: #282828; | |
47 | + width: 60%; | |
48 | + } | |
49 | + table{ | |
50 | + width: 100%; | |
51 | + } | |
52 | + .form-item{ | |
53 | + padding:0; | |
54 | + margin:0; | |
55 | + margin-bottom: 10px; | |
56 | + } | |
57 | + .btn-container .btn{ | |
58 | + margin-top: 20px; | |
59 | + height: 48px; | |
60 | + width: 320px; | |
61 | + } | |
62 | +</style> | |
63 | + | |
64 | +<template> | |
65 | + <div class="warp bg-gray"> | |
66 | + <section class="header bg-header"> | |
67 | + <i class="iconfont icon-back" @click="denied"></i> | |
68 | + <p>{{$t('enable.title')}}</p> | |
69 | + </section> | |
70 | + | |
71 | + <section class="content bg-white"> | |
72 | + <table> | |
73 | + <tbody> | |
74 | + <tr class="row"> | |
75 | + <td class="col label">{{$t('enable.domain')}}</td> | |
76 | + <td class="col value">{{prompt.domain}}</td> | |
77 | + </tr> | |
78 | + </tbody> | |
79 | + </table> | |
80 | + </section> | |
81 | + | |
82 | + <section class="content bg-white"> | |
83 | + <div class="message">{{$t('enable.message')}}</div> | |
84 | + </section> | |
85 | + | |
86 | + <div class="row btn-container" style="bottom: 20px; left: 20px; position: absolute;"> | |
87 | + <div class="btn" @click="denied">{{$t('enable.cancel')}}</div> | |
88 | + <div class="btn bg-green" @click="accepted">{{$t('enable.confirm')}}</div> | |
89 | + </div> | |
90 | + | |
91 | + </div> | |
92 | +</template> | |
93 | + | |
94 | +<script> | |
95 | +import { BTM } from "@/utils/constants"; | |
96 | +import {apis} from '@/utils/BrowserApis'; | |
97 | +import NotificationService from '../../services/NotificationService' | |
98 | + | |
99 | + | |
100 | +export default { | |
101 | + data() { | |
102 | + return { | |
103 | + prompt:'' | |
104 | + }; | |
105 | + }, | |
106 | + computed: { | |
107 | + }, | |
108 | + watch: { | |
109 | + }, | |
110 | + methods: { | |
111 | + accepted(){ | |
112 | + this.prompt.responder(true); | |
113 | + NotificationService.close(); | |
114 | + }, | |
115 | + denied(){ | |
116 | + this.prompt.responder(false); | |
117 | + NotificationService.close(); | |
118 | + } | |
119 | + }, mounted() { | |
120 | + this.prompt = window.data || apis.extension.getBackgroundPage().notification || null; | |
121 | + console.log(this.prompt) | |
122 | + } | |
123 | +}; | |
124 | +</script> |