mistAPI.js 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. /**
  2. @module MistAPI
  3. */
  4. const _ = require('underscore');
  5. const { ipcRenderer } = require('electron');
  6. const packageJson = require('./../../../package.json');
  7. module.exports = () => {
  8. let queue = [];
  9. const prefix = 'entry_';
  10. const MIST_SUBMENU_LIMIT = 100;
  11. // todo: error handling
  12. const filterAdd = options => {
  13. if (!(options instanceof Object)) {
  14. return false;
  15. }
  16. return ['name'].every(e => e in options);
  17. };
  18. // filterId the id to only contain a-z A-Z 0-9
  19. const filterId = str => {
  20. const filteredStr = String(str);
  21. let newStr = '';
  22. if (filteredStr) {
  23. for (let i = 0; i < filteredStr.length; i += 1) {
  24. if (/[a-zA-Z0-9_-]/.test(filteredStr.charAt(i))) {
  25. newStr += filteredStr.charAt(i);
  26. }
  27. }
  28. }
  29. return newStr;
  30. };
  31. /**
  32. Mist API
  33. Provides an API for all dapps, which specifically targets features from the Mist browser
  34. @class mist
  35. @constructor
  36. */
  37. const mist = {
  38. callbacks: {},
  39. version: packageJson.version,
  40. license: packageJson.license,
  41. platform: process.platform,
  42. requestAccount(callback) {
  43. if (callback) {
  44. if (!this.callbacks.connectAccount) {
  45. this.callbacks.connectAccount = [];
  46. }
  47. this.callbacks.connectAccount.push(callback);
  48. }
  49. ipcRenderer.send('mistAPI_requestAccount');
  50. },
  51. solidity: {
  52. version: String(packageJson.dependencies.solc).match(/\d+\.\d+\.\d+/)[0]
  53. },
  54. sounds: {
  55. bip: function playSound() {
  56. ipcRenderer.sendToHost(
  57. 'mistAPI_sound',
  58. `file://${__dirname}/../../../sounds/bip.mp3`
  59. );
  60. },
  61. bloop: function playSound() {
  62. ipcRenderer.sendToHost(
  63. 'mistAPI_sound',
  64. `file://${__dirname}/../../../sounds/bloop.mp3`
  65. );
  66. },
  67. invite: function playSound() {
  68. ipcRenderer.sendToHost(
  69. 'mistAPI_sound',
  70. `file://${__dirname}/../../../sounds/invite.mp3`
  71. );
  72. }
  73. },
  74. menu: {
  75. entries: {},
  76. /**
  77. Sets the badge text for the apps menu button
  78. Example
  79. mist.menu.setBadge('Some Text')
  80. @method setBadge
  81. @param {String} text
  82. */
  83. setBadge(text) {
  84. ipcRenderer.sendToHost('mistAPI_setBadge', text);
  85. },
  86. /**
  87. Adds/Updates a menu entry
  88. Example
  89. mist.menu.add('tkrzU', {
  90. name: 'My Meny Entry',
  91. badge: 50,
  92. position: 1,
  93. selected: true
  94. }, function(){
  95. // Router.go('/chat/1245');
  96. })
  97. @method add
  98. @param {String} id The id of the menu, has to be the same accross page reloads.
  99. @param {Object} options The menu options like {badge: 23, name: 'My Entry'}
  100. @param {Function} callback Change the callback to be called when the menu is pressed.
  101. */
  102. add(id, options, callback) {
  103. const args = Array.prototype.slice.call(arguments);
  104. callback = _.isFunction(args[args.length - 1]) ? args.pop() : null;
  105. options = _.isObject(args[args.length - 1]) ? args.pop() : null;
  106. id =
  107. _.isString(args[args.length - 1]) || _.isFinite(args[args.length - 1])
  108. ? args.pop()
  109. : null;
  110. if (!filterAdd(options)) {
  111. return false;
  112. }
  113. const filteredId = prefix + filterId(id);
  114. // restricting to 100 menu entries
  115. if (
  116. !(filteredId in this.entries) &&
  117. Object.keys(this.entries).length >= MIST_SUBMENU_LIMIT
  118. ) {
  119. return false;
  120. }
  121. const entry = {
  122. id: filteredId || 'mist_defaultId',
  123. position: options.position,
  124. selected: !!options.selected,
  125. name: options.name,
  126. badge: options.badge
  127. };
  128. queue.push({
  129. action: 'addMenu',
  130. entry
  131. });
  132. if (callback) {
  133. entry.callback = callback;
  134. }
  135. this.entries[filteredId] = entry;
  136. return true;
  137. },
  138. /**
  139. Updates a menu entry from the mist sidebar.
  140. @method update
  141. @param {String} id The id of the menu, has to be the same accross page reloads.
  142. @param {Object} options The menu options like {badge: 23, name: 'My Entry'}
  143. @param {Function} callback Change the callback to be called when the menu is pressed.
  144. */
  145. update() {
  146. this.add.apply(this, arguments);
  147. },
  148. /**
  149. Removes a menu entry from the mist sidebar.
  150. @method remove
  151. @param {String} id
  152. @param {String} id The id of the menu, has to be the same accross page reloads.
  153. @param {Object} options The menu options like {badge: 23, name: 'My Entry'}
  154. @param {Function} callback Change the callback to be called when the menu is pressed.
  155. */
  156. remove(id) {
  157. const filteredId = prefix + filterId(id);
  158. delete this.entries[filteredId];
  159. queue.push({
  160. action: 'removeMenu',
  161. filteredId
  162. });
  163. },
  164. /**
  165. Marks a menu entry as selected
  166. @method select
  167. @param {String} id
  168. */
  169. select(id) {
  170. const filteredId = prefix + filterId(id);
  171. queue.push({ action: 'selectMenu', id: filteredId });
  172. for (const e in this.entries) {
  173. if ({}.hasOwnProperty.call(this.entries, e)) {
  174. this.entries[e].selected = e === filteredId;
  175. }
  176. }
  177. },
  178. /**
  179. Removes all menu entries.
  180. @method clear
  181. */
  182. clear() {
  183. this.entries = {};
  184. queue.push({ action: 'clearMenu' });
  185. }
  186. }
  187. };
  188. ipcRenderer.on('mistAPI_callMenuFunction', (e, id) => {
  189. if (mist.menu.entries[id] && mist.menu.entries[id].callback) {
  190. mist.menu.entries[id].callback();
  191. }
  192. });
  193. ipcRenderer.on('uiAction_windowMessage', (e, type, error, value) => {
  194. console.log('uiAction_windowMessage', type, error, value);
  195. if (mist.callbacks[type]) {
  196. mist.callbacks[type].forEach(cb => {
  197. cb(error, value);
  198. });
  199. delete mist.callbacks[type];
  200. }
  201. });
  202. // work up queue every 500ms
  203. setInterval(() => {
  204. if (queue.length > 0) {
  205. ipcRenderer.sendToHost('mistAPI_menuChanges', queue);
  206. queue = [];
  207. }
  208. }, 500);
  209. return mist;
  210. };