├── src ├── components │ ├── .gitkeep │ ├── controls │ │ ├── custodian-payments.vue │ │ ├── help-btn.vue │ │ ├── menus │ │ │ ├── member-menu.vue │ │ │ ├── dev-menu.vue │ │ │ ├── main-menu.vue │ │ │ └── custodian-menu.vue │ │ ├── asset-input.vue │ │ ├── sleep-handler.vue │ │ ├── firehose.vue │ │ ├── lang-selector.vue │ │ ├── network-switcher.vue │ │ ├── member-select.vue │ │ ├── abi-viewer.vue │ │ ├── file-input.vue │ │ ├── table-parser.vue │ │ ├── trigger-newperiod.vue │ │ └── color-picker.vue │ └── ui │ │ ├── line-chart.vue │ │ ├── xspan.vue │ │ ├── time-zone.vue │ │ ├── zzz-sleep.vue │ │ ├── debug-data.vue │ │ ├── period-timer.vue │ │ ├── token-statistics.vue │ │ ├── auth-display.vue │ │ ├── vote-animation.vue │ │ ├── profile-pic.vue │ │ ├── action-parser.vue │ │ ├── social-links.vue │ │ ├── voting-progress.vue │ │ ├── request-signature.vue │ │ ├── financial-account.vue │ │ ├── display-action.vue │ │ └── transaction-overlay.vue ├── plugins │ ├── .gitkeep │ ├── axios.js │ ├── vuelidate.js │ ├── medium-editor.js │ ├── idle-vue.js │ ├── i18n.js │ ├── helper.js │ ├── profile-cache.js │ └── config-loader.js ├── i18n │ ├── .i18n-editor-metadata │ ├── index.js │ ├── compare-language-files.php │ ├── it │ │ └── index.json │ └── de │ │ └── index.json ├── modules │ ├── validators.js │ ├── wp_state_enum.js │ ├── notify-wrapper.js │ ├── nodeselector.js │ └── firehose-client.js ├── assets │ └── icon-fonts │ │ ├── eosdac-iconfont-v1-9 │ │ └── fonts │ │ │ ├── eosdac-iconfont-v1-9.eot │ │ │ ├── eosdac-iconfont-v1-9.ttf │ │ │ └── eosdac-iconfont-v1-9.woff │ │ └── extended_material_icons │ │ ├── fonts │ │ ├── materialdesignicons-webfont.eot │ │ ├── materialdesignicons-webfont.ttf │ │ ├── materialdesignicons-webfont.woff │ │ └── materialdesignicons-webfont.woff2 │ │ ├── scss │ │ ├── materialdesignicons.scss │ │ ├── _icons.scss │ │ ├── _core.scss │ │ ├── _functions.scss │ │ ├── _path.scss │ │ ├── _animated.scss │ │ └── _extras.scss │ │ └── license.md ├── css │ ├── themes │ │ ├── custom_theme_colors.styl │ │ ├── variables.ios.styl │ │ ├── variables.mat.styl │ │ └── common.variables.styl │ ├── json-pretty-html.css │ └── app.styl ├── store │ ├── ui │ │ ├── state.js │ │ ├── index.js │ │ ├── getters.js │ │ ├── mutations.js │ │ └── actions.js │ ├── dac │ │ ├── index.js │ │ ├── mutations.js │ │ ├── state.js │ │ └── getters.js │ ├── global │ │ ├── index.js │ │ ├── state.js │ │ ├── getters.js │ │ └── mutations.js │ ├── user │ │ ├── index.js │ │ ├── state.js │ │ ├── mutations.js │ │ └── getters.js │ └── index.js ├── pages │ ├── dev │ │ ├── transactions.vue │ │ ├── playground.vue │ │ └── dev-tools-home.vue │ ├── custodian │ │ ├── advanced.vue │ │ ├── dac-management.vue │ │ ├── contracts-config.vue │ │ └── explore-dac.vue │ └── Error404.vue ├── router │ ├── index.js │ ├── guards.js │ └── routes.js ├── statics │ ├── settings.json │ └── images │ │ └── eosdac_logo_notext.svg ├── index.template.html ├── App.vue └── layouts │ └── dacLayout.vue ├── .editorconfig ├── .postcssrc.js ├── .gitignore ├── check_language_strings.php ├── .babelrc ├── LICENSE ├── .eslintrc.js ├── deploy.sh ├── package.json └── README.md /src/components/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/plugins/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/i18n/.i18n-editor-metadata: -------------------------------------------------------------------------------- 1 | minify_resources=0 2 | resource_type=JSON 3 | resource_name=index 4 | -------------------------------------------------------------------------------- /src/plugins/axios.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | export default ({ Vue }) => { 4 | Vue.prototype.$axios = axios; 5 | }; 6 | -------------------------------------------------------------------------------- /src/plugins/vuelidate.js: -------------------------------------------------------------------------------- 1 | import Vuelidate from "vuelidate"; 2 | 3 | export default ({ Vue }) => { 4 | Vue.use(Vuelidate); 5 | }; 6 | -------------------------------------------------------------------------------- /src/modules/validators.js: -------------------------------------------------------------------------------- 1 | export function isEosName(accountname) { 2 | const re = /^[a-z1-5.]{1,12}$/; 3 | return re.test(accountname); 4 | } 5 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /src/assets/icon-fonts/eosdac-iconfont-v1-9/fonts/eosdac-iconfont-v1-9.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eosdac/eosdac-client-legacy/HEAD/src/assets/icon-fonts/eosdac-iconfont-v1-9/fonts/eosdac-iconfont-v1-9.eot -------------------------------------------------------------------------------- /src/assets/icon-fonts/eosdac-iconfont-v1-9/fonts/eosdac-iconfont-v1-9.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eosdac/eosdac-client-legacy/HEAD/src/assets/icon-fonts/eosdac-iconfont-v1-9/fonts/eosdac-iconfont-v1-9.ttf -------------------------------------------------------------------------------- /src/assets/icon-fonts/eosdac-iconfont-v1-9/fonts/eosdac-iconfont-v1-9.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eosdac/eosdac-client-legacy/HEAD/src/assets/icon-fonts/eosdac-iconfont-v1-9/fonts/eosdac-iconfont-v1-9.woff -------------------------------------------------------------------------------- /src/assets/icon-fonts/extended_material_icons/fonts/materialdesignicons-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eosdac/eosdac-client-legacy/HEAD/src/assets/icon-fonts/extended_material_icons/fonts/materialdesignicons-webfont.eot -------------------------------------------------------------------------------- /src/assets/icon-fonts/extended_material_icons/fonts/materialdesignicons-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eosdac/eosdac-client-legacy/HEAD/src/assets/icon-fonts/extended_material_icons/fonts/materialdesignicons-webfont.ttf -------------------------------------------------------------------------------- /src/assets/icon-fonts/extended_material_icons/fonts/materialdesignicons-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eosdac/eosdac-client-legacy/HEAD/src/assets/icon-fonts/extended_material_icons/fonts/materialdesignicons-webfont.woff -------------------------------------------------------------------------------- /src/assets/icon-fonts/extended_material_icons/fonts/materialdesignicons-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eosdac/eosdac-client-legacy/HEAD/src/assets/icon-fonts/extended_material_icons/fonts/materialdesignicons-webfont.woff2 -------------------------------------------------------------------------------- /src/plugins/medium-editor.js: -------------------------------------------------------------------------------- 1 | import MediumEditor from "vue2-medium-editor"; 2 | 3 | // leave the export, even if you don't use it 4 | export default ({ app, router, Vue }) => { 5 | Vue.component("medium-editor", MediumEditor); 6 | }; 7 | -------------------------------------------------------------------------------- /.postcssrc.js: -------------------------------------------------------------------------------- 1 | // https://github.com/michael-ciniawsky/postcss-load-config 2 | 3 | module.exports = { 4 | plugins: [ 5 | // to edit target browsers: use "browserslist" field in package.json 6 | require('autoprefixer') 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /src/modules/wp_state_enum.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | pending_approval: 0, 3 | work_in_progress: 1, 4 | pending_claim: 2, 5 | approved: 3, 6 | claimable: 4, 7 | expired: 5, 8 | cancelled: 100, 9 | claimed: 101 10 | }; 11 | -------------------------------------------------------------------------------- /src/components/controls/custodian-payments.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 13 | -------------------------------------------------------------------------------- /src/assets/icon-fonts/extended_material_icons/scss/materialdesignicons.scss: -------------------------------------------------------------------------------- 1 | /* MaterialDesignIcons.com */ 2 | @import "variables"; 3 | @import "functions"; 4 | @import "path"; 5 | @import "core"; 6 | @import "icons"; 7 | @import "extras"; 8 | @import "animated"; -------------------------------------------------------------------------------- /src/css/themes/custom_theme_colors.styl: -------------------------------------------------------------------------------- 1 | //This file is generated in the pre build script. 2 | 3 | $primary = #4a1289 4 | $bg1 = #1e2128 5 | $bg2 = #272b35 6 | $text1 = #f5f5f5 7 | $text2 = #c2c2c2 8 | $info = #31ccec 9 | $positive = #21ba45 10 | $negative = #db2828 11 | $dark = #272b36 12 | -------------------------------------------------------------------------------- /src/assets/icon-fonts/extended_material_icons/scss/_icons.scss: -------------------------------------------------------------------------------- 1 | @each $key, $value in $mdi-icons { 2 | .#{$mdi-css-prefix}-#{$key}:before { 3 | content: char($value); 4 | } 5 | } 6 | 7 | .#{$mdi-css-prefix}-blank:before { 8 | content: "\F68C"; 9 | visibility: hidden; 10 | } -------------------------------------------------------------------------------- /src/store/ui/state.js: -------------------------------------------------------------------------------- 1 | const theme_extensions_file = require("../../extensions/statics/config/theme.json"); 2 | 3 | export default { 4 | // 5 | enableTransactionOverlay: true, 6 | showTransactionOverlay: false, 7 | drawerIsOpen: true, 8 | isDark: theme_extensions_file.is_dark 9 | }; 10 | -------------------------------------------------------------------------------- /src/store/ui/index.js: -------------------------------------------------------------------------------- 1 | import state from "./state"; 2 | import * as getters from "./getters"; 3 | import * as mutations from "./mutations"; 4 | import * as actions from "./actions"; 5 | 6 | export default { 7 | namespaced: true, 8 | state, 9 | getters, 10 | mutations, 11 | actions 12 | }; 13 | -------------------------------------------------------------------------------- /src/css/themes/variables.ios.styl: -------------------------------------------------------------------------------- 1 | // App Shared Variables 2 | // -------------------------------------------------- 3 | // Shared Stylus variables go in the common.variables.styl file 4 | @import 'common.variables' 5 | 6 | // iOS only Quasar variables overwrites 7 | // ----------------------------------------- 8 | -------------------------------------------------------------------------------- /src/store/dac/index.js: -------------------------------------------------------------------------------- 1 | import state from "./state"; 2 | import * as getters from "./getters"; 3 | import * as mutations from "./mutations"; 4 | import * as actions from "./actions"; 5 | 6 | export default { 7 | namespaced: true, 8 | state, 9 | getters, 10 | mutations, 11 | actions 12 | }; 13 | -------------------------------------------------------------------------------- /src/store/global/index.js: -------------------------------------------------------------------------------- 1 | import state from "./state"; 2 | import * as getters from "./getters"; 3 | import * as mutations from "./mutations"; 4 | import * as actions from "./actions"; 5 | 6 | export default { 7 | namespaced: true, 8 | state, 9 | getters, 10 | mutations, 11 | actions 12 | }; 13 | -------------------------------------------------------------------------------- /src/store/user/index.js: -------------------------------------------------------------------------------- 1 | import state from "./state"; 2 | import * as getters from "./getters"; 3 | import * as mutations from "./mutations"; 4 | import * as actions from "./actions"; 5 | 6 | export default { 7 | namespaced: true, 8 | state, 9 | getters, 10 | mutations, 11 | actions 12 | }; 13 | -------------------------------------------------------------------------------- /src/css/themes/variables.mat.styl: -------------------------------------------------------------------------------- 1 | // App Shared Variables 2 | // -------------------------------------------------- 3 | // Shared Stylus variables go in the common.variables.styl file 4 | @import 'common.variables' 5 | 6 | // Material only Quasar variables overwrites 7 | // ----------------------------------------- 8 | -------------------------------------------------------------------------------- /src/store/global/state.js: -------------------------------------------------------------------------------- 1 | export default { 2 | // 3 | nodeInfo: null, 4 | networks: require("../../extensions/statics/config/networks.json"), 5 | dapp_version: null, 6 | local_storage_version: null, 7 | active_network: process.env.DEFAULT_NETWORK || "mainnet", 8 | chainId: null, 9 | scatter: null, 10 | eosApi: false, 11 | eosScatter: false 12 | }; 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .quasar 2 | .DS_Store 3 | .thumbs.db 4 | node_modules 5 | src/extensions*/ 6 | /dist 7 | /src-cordova/node_modules 8 | /src-cordova/platforms 9 | /src-cordova/plugins 10 | /src-cordova/www 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | -------------------------------------------------------------------------------- /src/store/ui/getters.js: -------------------------------------------------------------------------------- 1 | export function getIsDark(state) { 2 | return state.isDark; 3 | } 4 | 5 | export function getShowTransactionOverlay(state) { 6 | return state.showTransactionOverlay; 7 | } 8 | 9 | export function getEnableTransactionOverlay(state) { 10 | return state.enableTransactionOverlay; 11 | } 12 | 13 | export function getDrawerIsOpen(state) { 14 | return state.drawerIsOpen; 15 | } 16 | -------------------------------------------------------------------------------- /src/plugins/idle-vue.js: -------------------------------------------------------------------------------- 1 | import IdleVue from "idle-vue"; 2 | 3 | export default ({ Vue, store }) => { 4 | const eventsHub = new Vue(); 5 | const options = { 6 | eventEmitter: eventsHub, 7 | idleTime: 1000 * 60 * 5, //enter sleep state 8 | store: store, 9 | startAtIdle: false, 10 | events: ["keydown", "mousedown", "scroll", "touchstart"] 11 | }; 12 | Vue.use(IdleVue, options); 13 | }; 14 | -------------------------------------------------------------------------------- /src/pages/dev/transactions.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 18 | -------------------------------------------------------------------------------- /src/pages/custodian/advanced.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 18 | -------------------------------------------------------------------------------- /src/assets/icon-fonts/extended_material_icons/scss/_core.scss: -------------------------------------------------------------------------------- 1 | .#{$mdi-css-prefix}:before, 2 | .#{$mdi-css-prefix}-set { 3 | display: inline-block; 4 | font: normal normal normal #{$mdi-font-size-base}/1 '#{$mdi-font-name}'; // shortening font declaration 5 | font-size: inherit; // can't have font-size inherit on line above, so need to override 6 | text-rendering: auto; // optimizelegibility throws things off #1094 7 | line-height: inherit; 8 | -webkit-font-smoothing: antialiased; 9 | -moz-osx-font-smoothing: grayscale; 10 | } -------------------------------------------------------------------------------- /src/i18n/index.js: -------------------------------------------------------------------------------- 1 | import de from "./de"; 2 | import enGB from "./en_GB"; 3 | import enUS from "./en_US"; 4 | import es from "./es"; 5 | import fr from "./fr"; 6 | import it from "./it"; 7 | import ja from "./ja"; 8 | import ko from "./ko"; 9 | import ru from "./ru"; 10 | import vi from "./vi"; 11 | import zhHANS from "./zh_CN"; 12 | export default { 13 | de: de, 14 | "en-gb": enGB, 15 | "en-us": enUS, 16 | es: es, 17 | fr: fr, 18 | it: it, 19 | ja: ja, 20 | ko: ko, 21 | ru: ru, 22 | vi: vi, 23 | "zh-hans": zhHANS 24 | }; 25 | -------------------------------------------------------------------------------- /check_language_strings.php: -------------------------------------------------------------------------------- 1 | $strings) { 8 | foreach ($strings as $key => $string) { 9 | $key_string = "$category.$key"; 10 | $command = "grep -r \"$key_string\" src\n"; 11 | $output = shell_exec($command); 12 | if ($output == "" && $category != 'contract_errors') { 13 | print "Not used: $key_string\n"; 14 | } 15 | //print $command . "\n"; 16 | //print "|" . $output . "|\n"; 17 | } 18 | } -------------------------------------------------------------------------------- /src/components/ui/line-chart.vue: -------------------------------------------------------------------------------- 1 | 29 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", { 5 | "modules": false, 6 | "loose": false, 7 | "useBuiltIns": "usage" 8 | } 9 | ], 10 | [ 11 | "@babel/preset-stage-2", { 12 | "modules": false, 13 | "loose": false, 14 | "useBuiltIns": true, 15 | "decoratorsLegacy": true 16 | } 17 | ] 18 | ], 19 | "plugins": [ 20 | [ 21 | "@babel/transform-runtime", { 22 | "polyfill": false, 23 | "regenerator": false 24 | } 25 | ] 26 | ], 27 | "comments": false 28 | } 29 | -------------------------------------------------------------------------------- /src/store/ui/mutations.js: -------------------------------------------------------------------------------- 1 | export function setActiveTheme(state, payload) { 2 | state.activeTheme = payload; 3 | } 4 | 5 | export function setIsDark(state, payload) { 6 | state.isDark = payload; 7 | } 8 | 9 | export function setShowTransactionOverlay(state, payload) { 10 | if (state.enableTransactionOverlay) { 11 | state.showTransactionOverlay = payload; 12 | } 13 | } 14 | 15 | export function setEnableTransactionOverlay(state, payload) { 16 | state.enableTransactionOverlay = payload; 17 | } 18 | 19 | export function setDrawerIsOpen(state, payload) { 20 | state.drawerIsOpen = payload; 21 | } 22 | -------------------------------------------------------------------------------- /src/components/ui/xspan.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 28 | -------------------------------------------------------------------------------- /src/assets/icon-fonts/extended_material_icons/scss/_functions.scss: -------------------------------------------------------------------------------- 1 | @function char($character-code) { 2 | @if function-exists("selector-append") { 3 | @return unquote("\"\\#{$character-code}\""); 4 | } 5 | 6 | @if "\\#{'x'}" == "\\x" { 7 | @return str-slice("\x", 1, 1) + $character-code; 8 | } 9 | @else { 10 | @return #{"\"\\"}#{$character-code + "\""}; 11 | } 12 | } 13 | 14 | @function mdi($name) { 15 | @if map-has-key($mdi-icons, $name) == false { 16 | @warn "Icon #{$name} not found."; 17 | @return ""; 18 | } 19 | @return char(map-get($mdi-icons, $name)); 20 | } -------------------------------------------------------------------------------- /src/css/json-pretty-html.css: -------------------------------------------------------------------------------- 1 | 2 | .json-pretty { 3 | 4 | font-family: Menlo, Monaco, "Courier New", monospace; 5 | font-weight: normal; 6 | font-size: 14px; 7 | line-height: 16px; 8 | letter-spacing: 0; 9 | 10 | text-align: left; 11 | 12 | padding-top: 10px; 13 | padding-bottom: 10px; 14 | margin: 0; 15 | } 16 | .json-selected { 17 | background-color: rgba(139, 191, 228, 0.19999999999999996); 18 | } 19 | 20 | .json-string { 21 | color: #6caedd; 22 | } 23 | 24 | .json-key { 25 | color: #ec5f67; 26 | } 27 | 28 | .json-boolean { 29 | color: #99c794; 30 | } 31 | 32 | .json-number { 33 | color: #99c794; 34 | } 35 | 36 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import VueRouter from "vue-router"; 3 | 4 | import routes from "./routes"; 5 | 6 | Vue.use(VueRouter); 7 | 8 | /* 9 | * If not building with SSR mode, you can 10 | * directly export the Router instantiation 11 | */ 12 | 13 | export default function(/* { store, ssrContext } */) { 14 | const Router = new VueRouter({ 15 | scrollBehavior: () => ({ y: 0 }), 16 | routes, 17 | 18 | // Leave these as is and change from quasar.conf.js instead! 19 | // quasar.conf.js -> build -> vueRouterMode 20 | mode: process.env.VUE_ROUTER_MODE, 21 | base: process.env.VUE_ROUTER_BASE 22 | }); 23 | 24 | return Router; 25 | } 26 | -------------------------------------------------------------------------------- /src/modules/notify-wrapper.js: -------------------------------------------------------------------------------- 1 | import { Notify } from 'quasar' 2 | 3 | export function info(message){ 4 | Notify.create({ 5 | message: `${message}`, 6 | timeout: 2000, 7 | type: 'info', 8 | position: 'bottom-right' 9 | }); 10 | } 11 | 12 | export function error(message){ 13 | Notify.create({ 14 | message: `${message}`, 15 | timeout: 2000, 16 | type: 'negative', 17 | position: 'bottom-right' 18 | }); 19 | } 20 | 21 | export function success(message){ 22 | Notify.create({ 23 | message: `${message}`, 24 | timeout: 2000, 25 | type: 'positive', 26 | position: 'bottom-right' 27 | }); 28 | } 29 | 30 | -------------------------------------------------------------------------------- /src/router/guards.js: -------------------------------------------------------------------------------- 1 | import store from "../store"; 2 | 3 | const Guards = { 4 | logInCheck(to, from, next) { 5 | if (!store.getters["user/getAccountName"]) { 6 | next({ path: "" }); 7 | } else { 8 | next(); 9 | } 10 | }, 11 | custodianCheck(to, from, next) { 12 | let isCustodian = store.getters["user/getIsCustodian"]; 13 | if (!isCustodian) { 14 | next({ path: "" }); 15 | } else { 16 | next(); 17 | } 18 | }, 19 | memberCheck(to, from, next) { 20 | let status = store.getters["user/getMemberStatus"]; 21 | if (status !== "member") { 22 | next({ path: "" }); 23 | } else { 24 | next(); 25 | } 26 | } 27 | }; 28 | 29 | export default Guards; 30 | -------------------------------------------------------------------------------- /src/store/user/state.js: -------------------------------------------------------------------------------- 1 | let settings = require("../../statics/settings.json"); 2 | // let extended_settings; 3 | // try { 4 | // extended_settings = require("../../extensions/statics/config/settings.json"); 5 | // } catch (e) { 6 | // extended_settings = []; 7 | // } 8 | 9 | export default { 10 | // 11 | isIdle: null, 12 | language: false, 13 | isLoaded: false, 14 | accountName: null, 15 | profilePicture: null, 16 | account: null, 17 | dacBalance: null, 18 | systemBalance: null, 19 | agreedTermsVersion: null, 20 | lastTransaction: null, 21 | isCandidate: null, 22 | dacVotes: null, 23 | 24 | msigIsSeenCache: [], 25 | msigTransferQeue: [], 26 | 27 | settings: settings, 28 | catDelegations: null 29 | }; 30 | -------------------------------------------------------------------------------- /src/store/ui/actions.js: -------------------------------------------------------------------------------- 1 | import { colors } from "quasar"; 2 | 3 | // { 4 | // "is_dark": false, 5 | // "colors": { 6 | // "$primary": "#6677d9", 7 | // "$bg1": "#d7dcf7", 8 | // "$bg2": "#f0f1fa", 9 | // "$text1": "#292829", 10 | // "$text2": "#332f33", 11 | // "$info": "#949ed4", 12 | // "$positive": "#78c48a", 13 | // "$negative": "#d67878", 14 | // "$dark": "#afb6e0" 15 | // } 16 | // } 17 | 18 | export async function loadTheme({ commit }, theme) { 19 | let colorvars = Object.keys(theme.colors); 20 | 21 | commit("setIsDark", theme.isDark); 22 | for (let i = 0; i < colorvars.length; i++) { 23 | let cv = colorvars[i].replace("$", ""); 24 | colors.setBrand(cv, theme.colors[colorvars[i]]); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/assets/icon-fonts/extended_material_icons/scss/_path.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: '#{$mdi-font-name}'; 3 | src: url('#{$mdi-font-path}/#{$mdi-filename}-webfont.eot?v=#{$mdi-version}'); 4 | src: url('#{$mdi-font-path}/#{$mdi-filename}-webfont.eot?#iefix&v=#{$mdi-version}') format('embedded-opentype'), 5 | url('#{$mdi-font-path}/#{$mdi-filename}-webfont.woff2?v=#{$mdi-version}') format('woff2'), 6 | url('#{$mdi-font-path}/#{$mdi-filename}-webfont.woff?v=#{$mdi-version}') format('woff'), 7 | url('#{$mdi-font-path}/#{$mdi-filename}-webfont.ttf?v=#{$mdi-version}') format('truetype'), 8 | url('#{$mdi-font-path}/#{$mdi-filename}-webfont.svg?v=#{$mdi-version}##{$mdi-filename}#{$mdi-font-weight}') format('svg'); 9 | font-weight: normal; 10 | font-style: normal; 11 | } 12 | -------------------------------------------------------------------------------- /src/assets/icon-fonts/extended_material_icons/scss/_animated.scss: -------------------------------------------------------------------------------- 1 | // From Font Awesome 2 | .#{$mdi-css-prefix}-spin:before { 3 | -webkit-animation: #{$mdi-css-prefix}-spin 2s infinite linear; 4 | animation: #{$mdi-css-prefix}-spin 2s infinite linear; 5 | } 6 | 7 | @-webkit-keyframes #{$mdi-css-prefix}-spin { 8 | 0% { 9 | -webkit-transform: rotate(0deg); 10 | transform: rotate(0deg); 11 | } 12 | 100% { 13 | -webkit-transform: rotate(359deg); 14 | transform: rotate(359deg); 15 | } 16 | } 17 | 18 | @keyframes #{$mdi-css-prefix}-spin { 19 | 0% { 20 | -webkit-transform: rotate(0deg); 21 | transform: rotate(0deg); 22 | } 23 | 100% { 24 | -webkit-transform: rotate(359deg); 25 | transform: rotate(359deg); 26 | } 27 | } -------------------------------------------------------------------------------- /src/pages/Error404.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 32 | -------------------------------------------------------------------------------- /src/components/ui/time-zone.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 39 | -------------------------------------------------------------------------------- /src/store/global/getters.js: -------------------------------------------------------------------------------- 1 | export function getNodeInfo(state, payload) { 2 | return state.nodeInfo; 3 | } 4 | export function getScatter(state) { 5 | return state.scatter; 6 | } 7 | 8 | export function getDacApi(state) { 9 | return state.eosApi; 10 | } 11 | 12 | export function getAccountName(state) { 13 | return state.accountName; 14 | } 15 | 16 | export function getActiveNetwork(state) { 17 | return state.networks.find(n => n.name == state.active_network); 18 | } 19 | 20 | export function getNetworks(state) { 21 | return state.networks; 22 | } 23 | 24 | export function getActiveNetworkName(state) { 25 | return state.active_network; 26 | } 27 | 28 | export function getDapp_version(state) { 29 | return state.dapp_version; 30 | } 31 | 32 | export function getLocal_storage_version(state) { 33 | return state.local_storage_version; 34 | } 35 | 36 | export function getChainId(state) { 37 | return state.chainId; 38 | } 39 | -------------------------------------------------------------------------------- /src/pages/custodian/dac-management.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/components/controls/help-btn.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (MIT) Copyright 2018 eosDAC 2 | https://opensource.org/licenses/mit-license.php 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Vuex from "vuex"; 3 | import createPersistedState from "vuex-persistedstate"; 4 | 5 | import global from "./global"; 6 | import user from "./user"; 7 | import dac from "./dac"; 8 | import ui from "./ui"; 9 | 10 | let store_extension; 11 | try { 12 | store_extension = require("../extensions/store/store_extension").default; 13 | } catch (e) { 14 | store_extension = { 15 | modules: {}, 16 | plugins: [] 17 | }; 18 | } 19 | 20 | Vue.use(Vuex); 21 | 22 | const store = new Vuex.Store({ 23 | modules: { 24 | ...store_extension.modules, 25 | global, 26 | user, 27 | dac, 28 | ui 29 | }, 30 | plugins: [ 31 | ...store_extension.plugins, 32 | createPersistedState({ 33 | key: "global", 34 | paths: [ 35 | "global.active_network", 36 | "global.dapp_version", 37 | "global.local_storage_version" 38 | ] 39 | }), 40 | createPersistedState({ 41 | key: "user", 42 | paths: ["user.settings", "user.language", "user.msigIsSeenCache"] 43 | }) 44 | ] 45 | }); 46 | 47 | export default store; 48 | -------------------------------------------------------------------------------- /src/i18n/compare-language-files.php: -------------------------------------------------------------------------------- 1 | $v) { 13 | if (is_array($v)) { 14 | $r[$k] = array_diff_key_recursive($a1[$k], $a2[$k]); 15 | } else { 16 | $r = @array_diff_key($a1, $a2); 17 | } 18 | if (is_array($r[$k]) && count($r[$k]) == 0) { 19 | unset($r[$k]); 20 | } 21 | } 22 | return $r; 23 | } 24 | 25 | $enus_json = getJson('en_US'); 26 | 27 | $dirs = array_filter(glob('*'), 'is_dir'); 28 | 29 | foreach ($dirs as $dir) { 30 | if ($dir != 'en_US') { 31 | print " ==== COMPARING $dir ====:\n"; 32 | $json = getJson($dir); 33 | $difference = array_diff_key_recursive($enus_json, $json); 34 | if (count($difference) == 0) { 35 | print $dir . " has the correct number of keys!\n"; 36 | } else { 37 | print $dir . " is missing the following keys:\n"; 38 | print_r($difference); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/components/controls/menus/member-menu.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 39 | -------------------------------------------------------------------------------- /src/store/global/mutations.js: -------------------------------------------------------------------------------- 1 | export function setNodeInfo(state, payload) { 2 | state.nodeInfo = payload; 3 | } 4 | export function setScatter(state, payload) { 5 | state.scatter = payload; 6 | } 7 | 8 | export function setDacApi(state, payload) { 9 | state.eosApi = payload; 10 | } 11 | 12 | export function setEosScatter(state, payload) { 13 | state.eosScatter = payload; 14 | } 15 | 16 | export function setActiveNetwork(state, payload) { 17 | state.active_network = payload; 18 | } 19 | 20 | export function setChainId(state, payload) { 21 | state.chainId = payload; 22 | } 23 | 24 | export function setDapp_version(state, payload) { 25 | state.dapp_version = payload; 26 | } 27 | export function setLocal_storage_version(state, payload) { 28 | state.local_storage_version = payload; 29 | } 30 | 31 | export function setNode(state, nodeurl) { 32 | let [protocol, host, port] = nodeurl.split(":"); 33 | 34 | let parts = { 35 | protocol: protocol, 36 | host: host.replace(/\//g, ""), 37 | port: port || (protocol === "https" ? "443" : "80") 38 | }; 39 | 40 | //set new node to the active network 41 | let n = state.networks.find(n => n.name == state.active_network); 42 | Object.assign(n, parts); 43 | 44 | //delete our apis 45 | state.eosApi = state.eosScatter = null; 46 | } 47 | -------------------------------------------------------------------------------- /src/components/controls/menus/dev-menu.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 45 | -------------------------------------------------------------------------------- /src/pages/custodian/contracts-config.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 52 | -------------------------------------------------------------------------------- /src/components/ui/zzz-sleep.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 36 | 37 | 56 | -------------------------------------------------------------------------------- /src/store/dac/mutations.js: -------------------------------------------------------------------------------- 1 | //dac store 2 | export function setIsLoaded(state, payload) { 3 | state.isLoaded = payload; 4 | } 5 | 6 | export function setMemberTerms(state, memberterms) { 7 | memberterms = memberterms.rows.sort(function(a, b) { 8 | return a.version - b.version; 9 | }); 10 | 11 | state.memberTerms = memberterms; 12 | } 13 | 14 | export function setTokenStats(state, payload) { 15 | state.tokenStats = payload; 16 | } 17 | 18 | export function setCustodians(state, payload) { 19 | state.custodians = payload; 20 | } 21 | 22 | export function setCustodianPermissions(state, payload) { 23 | state.custodianPermissions = payload; 24 | } 25 | 26 | export function setDacAdmins(state, payload) { 27 | state.dacAdmins = payload; 28 | } 29 | 30 | export function setCustodianConfig(state, payload) { 31 | state.custodianConfig = payload; 32 | } 33 | 34 | export function setWpConfig(state, payload) { 35 | state.wpConfig = payload; 36 | } 37 | 38 | export function setCandidates(state, payload) { 39 | state.candidates = payload; 40 | } 41 | 42 | export function setCandidateVote(state, payload) { 43 | let candidate_name = payload.candidate_name; 44 | let vote_amount = payload.total_votes; 45 | if (state.candidates) { 46 | let cand = state.candidates.find(c => c.candidate_name == candidate_name); 47 | cand.total_votes = vote_amount; 48 | } 49 | } 50 | 51 | export function setCustodianState(state, payload) { 52 | state.custodianState = payload; 53 | } 54 | -------------------------------------------------------------------------------- /src/store/dac/state.js: -------------------------------------------------------------------------------- 1 | export default { 2 | // 3 | isLoaded: false, 4 | memberTerms: null, 5 | custodians: null, 6 | candidates: null, 7 | custodianPermissions: null, 8 | dacAdmins: [], 9 | custodianConfig: { 10 | lockupasset: null, 11 | maxvotes: null, 12 | numelected: null, 13 | periodlength: null, 14 | authaccount: null, 15 | tokenholder: null, 16 | serviceprovider: null, 17 | should_pay_via_service_provider: null, 18 | initial_vote_quorum_percent: null, 19 | vote_quorum_percent: null, 20 | auth_threshold_high: null, 21 | auth_threshold_mid: null, 22 | auth_threshold_low: null, 23 | lockup_release_time_delay: null, 24 | requested_pay_max: null 25 | }, 26 | 27 | tokenStats: { 28 | maxSupply: null, 29 | supply: null, 30 | precision: null, 31 | symbol: null, 32 | transferLocked: null 33 | }, 34 | 35 | custodianState: { 36 | lastperiodtime: null, 37 | total_weight_of_votes: null, 38 | total_votes_on_candidates: null, 39 | number_active_candidates: null, 40 | met_initial_votes_threshold: null 41 | }, 42 | 43 | wpConfig: { 44 | service_account: null, 45 | authority_account: null, 46 | member_terms_account: null, 47 | treasury_account: null, 48 | proposal_threshold: null, 49 | proposal_approval_threshold_percent: null, 50 | claim_threshold: null, 51 | claim_approval_threshold_percent: null, 52 | escrow_expiry: null 53 | }, 54 | wpCategories: require("../../extensions/statics/config/wp_categories.json") 55 | }; 56 | -------------------------------------------------------------------------------- /src/plugins/i18n.js: -------------------------------------------------------------------------------- 1 | import nestedObjectAssign from "nested-object-assign"; 2 | import VueI18n from "vue-i18n"; 3 | 4 | import messages from "src/i18n"; 5 | import custom_messages from "src/extensions/i18n"; 6 | 7 | let msg = nestedObjectAssign({}, messages, custom_messages); 8 | 9 | export default ({ app, Vue, store }) => { 10 | Vue.use(VueI18n); 11 | let lang = "en-us"; 12 | 13 | if (store.getters["user/getLanguage"]) { 14 | lang = store.getters["user/getLanguage"]; 15 | } else { 16 | if (typeof msg[browserLocale()] === "undefined") { 17 | lang = "en-us"; 18 | } else { 19 | lang = browserLocale(); 20 | } 21 | } 22 | 23 | store.commit("user/setLanguage", lang); 24 | 25 | app.i18n = new VueI18n({ 26 | silentTranslationWarn: true, 27 | locale: lang, 28 | fallbackLocale: "en-us", 29 | messages: { 30 | de: msg["de"], 31 | "en-gb": msg["en-gb"], 32 | "en-us": msg["en-us"], 33 | es: msg["es"], 34 | fr: msg["fr"], 35 | it: msg["it"], 36 | ja: msg["ja"], 37 | ko: msg["ko"], 38 | ru: msg["ru"], 39 | vi: msg["vi"], 40 | "zh-hans": msg["zh-hans"] 41 | } 42 | }); 43 | Vue.prototype.i18n = app.i18n; 44 | }; 45 | 46 | function browserLocale() { 47 | let lang; 48 | if (navigator.languages && navigator.languages.length) { 49 | // latest versions of Chrome and Firefox set this correctly 50 | lang = navigator.languages[0]; 51 | } else if (navigator.userLanguage) { 52 | // IE only 53 | lang = navigator.userLanguage; 54 | } else { 55 | // latest versions of Chrome, Firefox, and Safari set this correctly 56 | lang = navigator.language; 57 | } 58 | return lang.toLowerCase(); 59 | } 60 | -------------------------------------------------------------------------------- /src/components/ui/debug-data.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 60 | -------------------------------------------------------------------------------- /src/pages/dev/playground.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 65 | -------------------------------------------------------------------------------- /src/pages/custodian/explore-dac.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /src/assets/icon-fonts/extended_material_icons/scss/_extras.scss: -------------------------------------------------------------------------------- 1 | $mdi-sizes: 18 24 36 48; 2 | @each $mdi-size in $mdi-sizes { 3 | .#{$mdi-css-prefix}-#{$mdi-size}px { 4 | &.#{$mdi-css-prefix}-set, 5 | &.#{$mdi-css-prefix}:before { 6 | font-size: $mdi-size * 1px; 7 | } 8 | } 9 | } 10 | 11 | .#{$mdi-css-prefix}-dark { 12 | &:before { 13 | color: rgba(0, 0, 0, 0.54); 14 | } 15 | &.mdi-inactive:before { 16 | color: rgba(0, 0, 0, 0.26); 17 | } 18 | } 19 | .#{$mdi-css-prefix}-light { 20 | &:before { 21 | color: rgba(255, 255, 255, 1); 22 | } 23 | &.mdi-inactive:before { 24 | color: rgba(255, 255, 255, 0.3); 25 | } 26 | } 27 | 28 | $mdi-degrees: 45 90 135 180 225 270 315; 29 | @each $mdi-degree in $mdi-degrees { 30 | .#{$mdi-css-prefix}-rotate-#{$mdi-degree}{ 31 | &:before { 32 | -webkit-transform: rotate(#{$mdi-degree}deg); 33 | -ms-transform: rotate(#{$mdi-degree}deg); 34 | transform: rotate(#{$mdi-degree}deg); 35 | } 36 | /* 37 | // Not included in production 38 | &.#{$mdi-css-prefix}-flip-h:before { 39 | -webkit-transform: scaleX(-1) rotate(#{$mdi-degree}deg); 40 | transform: scaleX(-1) rotate(#{$mdi-degree}deg); 41 | filter: FlipH; 42 | -ms-filter: "FlipH"; 43 | } 44 | &.#{$mdi-css-prefix}-flip-v:before { 45 | -webkit-transform: scaleY(-1) rotate(#{$mdi-degree}deg); 46 | -ms-transform: rotate(#{$mdi-degree}deg); 47 | transform: scaleY(-1) rotate(#{$mdi-degree}deg); 48 | filter: FlipV; 49 | -ms-filter: "FlipV"; 50 | } 51 | */ 52 | } 53 | } 54 | .#{$mdi-css-prefix}-flip-h:before { 55 | -webkit-transform: scaleX(-1); 56 | transform: scaleX(-1); 57 | filter: FlipH; 58 | -ms-filter: "FlipH"; 59 | } 60 | .#{$mdi-css-prefix}-flip-v:before { 61 | -webkit-transform: scaleY(-1); 62 | transform: scaleY(-1); 63 | filter: FlipV; 64 | -ms-filter: "FlipV"; 65 | } -------------------------------------------------------------------------------- /src/statics/settings.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "toolbar_menu_items", 4 | "type": "boolean", 5 | "value": true, 6 | "group": "toolbar" 7 | }, 8 | { 9 | "name": "toolbar_profile_image", 10 | "type": "boolean", 11 | "value": true, 12 | "group": "toolbar" 13 | }, 14 | { 15 | "name": "toolbar_reveal", 16 | "type": "boolean", 17 | "value": false, 18 | "group": "toolbar" 19 | }, 20 | { 21 | "name": "notify_dac_msg", 22 | "type": "boolean", 23 | "value": false, 24 | "group": "notify", 25 | "disabled": true 26 | }, 27 | { 28 | "name": "notify_success_msg", 29 | "type": "boolean", 30 | "value": true, 31 | "group": "notify" 32 | }, 33 | { 34 | "name": "notify_info_msg", 35 | "type": "boolean", 36 | "value": true, 37 | "group": "notify" 38 | }, 39 | { 40 | "name": "notify_error_msg", 41 | "type": "boolean", 42 | "value": true, 43 | "group": "notify", 44 | "disabled": true 45 | }, 46 | 47 | { 48 | "name": "debug_data_structure", 49 | "type": "boolean", 50 | "value": false, 51 | "group": "debug" 52 | }, 53 | { 54 | "name": "debug_dev_tools", 55 | "type": "boolean", 56 | "value": false, 57 | "group": "debug" 58 | }, 59 | { 60 | "name": "debug_console_log", 61 | "type": "boolean", 62 | "value": false, 63 | "group": "debug" 64 | }, 65 | 66 | { 67 | "name": "locale_numbers", 68 | "type": "boolean", 69 | "value": true, 70 | "group": "locale" 71 | }, 72 | { 73 | "name": "locale_language", 74 | "type": "component", 75 | "value": "langselector", 76 | "group": "locale" 77 | }, 78 | { 79 | "name": "trx_delay", 80 | "type": "number", 81 | "value": 0, 82 | "group": "advanced" 83 | }, 84 | { 85 | "name": "auto_logout_delay", 86 | "type": "number", 87 | "value": 0, 88 | "group": "advanced" 89 | } 90 | 91 | ] -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | 4 | parserOptions: { 5 | parser: 'babel-eslint', 6 | sourceType: 'module' 7 | }, 8 | 9 | env: { 10 | browser: true 11 | }, 12 | 13 | extends: ['plugin:vue/essential', '@vue/standard', 'prettier'], 14 | 15 | // required to lint *.vue files 16 | plugins: ['vue', 'prettier'], 17 | 18 | globals: { 19 | ga: true, // Google Analytics 20 | cordova: true, 21 | __statics: true 22 | }, 23 | 24 | // add your custom rules here 25 | rules: { 26 | // "standard/computed-property-even-spacing": "off", 27 | 'prettier/prettier': 'warn', 28 | // semi: 'on', 29 | // Temp Rules 30 | eqeqeq: 'off', 31 | 'spaced-comment': 'off', 32 | camelcase: 'off', 33 | 'standard/computed-property-even-spacing': 'off', 34 | 'no-empty-pattern': 'off', 35 | 'no-useless-escape': 'off', 36 | 'no-useless-return': 'off', 37 | 'vue/return-in-computed-property': 'off', 38 | 'vue/no-side-effects-in-computed-properties': 'off', 39 | 'standard/no-callback-literal': 'off', 40 | 'vue/require-prop-type-constructor': 'off', 41 | 'vue/no-use-v-if-with-v-for': 'off', 42 | 'vue/valid-v-on': 'off', 43 | // Temp Rules End 44 | 45 | // allow async-await 46 | 'generator-star-spacing': 'off', 47 | // allow paren-less arrow functions 48 | 'arrow-parens': 'off', 49 | 'one-var': 'off', 50 | 'prefer-promise-reject-errors': 'off', 51 | 52 | 'import/first': 'off', 53 | 'import/named': 'error', 54 | 'import/namespace': 'error', 55 | 'import/default': 'error', 56 | 'import/export': 'error', 57 | 'import/extensions': 'off', 58 | 'import/no-unresolved': 'off', 59 | 'import/no-extraneous-dependencies': 'off', 60 | 61 | // allow console.log during development only 62 | 'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off', 63 | // allow debugger during development only 64 | 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off' 65 | } 66 | }; 67 | -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo " " 4 | echo " " 5 | echo "===================== eosDAC Member Client Deploy Script =====================" 6 | echo " " 7 | echo "Usage: ./deploy.sh 1.0.0" 8 | echo "This example will deploy the 1.0.0 tag." 9 | echo " " 10 | echo "For local testing, add a true at the end like so:" 11 | echo "./deploy.sh master true" 12 | echo "This example will run the master branch locally." 13 | echo " " 14 | echo "Note: this script will blow away all local changes." 15 | echo " " 16 | echo "==============================================================================" 17 | echo " " 18 | echo " " 19 | 20 | CONFIG_FILE="" 21 | LOCAL_DEV=false 22 | 23 | if [ -z "$1" ] 24 | then 25 | echo "Please specify the branch or tag you'd like to deploy." 26 | fi 27 | 28 | if [[ "$2" == "true" ]] 29 | then 30 | echo "Local build, will run quasar dev." 31 | LOCAL_DEV=true 32 | fi 33 | 34 | rm -f deploy-complete 35 | 36 | git checkout . 37 | git checkout master 38 | if ! git pull 39 | then 40 | echo "======= ERROR: =======" 41 | echo "Looks like the repo is dirty?" 42 | echo "NOT DEPLOYED" 43 | echo "======================" 44 | exit 1 45 | fi 46 | git fetch --tags 47 | git checkout "$1" 48 | git pull 49 | 50 | if $LOCAL_DEV ; 51 | then 52 | yarn dev 53 | else 54 | mkdir -p dist/deploy 55 | yarn build 56 | if [ -z "$(ls -A dist/spa-mat)" ]; then 57 | echo "======= ERROR: =======" 58 | echo "dist/spa-mat empty." 59 | echo "Build Failed? NOT DEPLOYED" 60 | echo "======================" 61 | exit 1 62 | fi 63 | 64 | mv dist/deploy dist/deploy-old && mv dist/spa-mat dist/deploy && touch deploy-complete 65 | 66 | if [ -f deploy-complete ]; then 67 | rm -rf dist/deploy-old 68 | echo "Deploy Complete." 69 | echo "Please test and verify using the correct domain such as members-dev.eosdac.io, members-staging.eosdac.io, or members.eosdac.io" 70 | else 71 | echo "======= ERROR: =======" 72 | echo "Deploy folder move failed." 73 | echo "NOT DEPLOYED" 74 | echo "======================" 75 | rm -rf dist/deploy && mv dist/deploy-old dist/deploy 76 | exit 1 77 | fi 78 | 79 | 80 | fi 81 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "eosdacmemberclient", 3 | "version": "0.9.1", 4 | "license": "MIT", 5 | "local_storage_version": "0.1", 6 | "description": "Toolkit GUI for eosDAC", 7 | "productName": "eosDAC Member Client", 8 | "cordovaId": "org.cordova.quasar.app", 9 | "author": "eosDAC ", 10 | "private": true, 11 | "scripts": { 12 | "build": "node ./build_scripts/before.js && quasar build", 13 | "dev": "node ./build_scripts/before.js && quasar dev", 14 | "add_extensions": "git -C ./src clone" 15 | }, 16 | "dependencies": { 17 | "@chenfengyuan/vue-countdown": "^1.1.2", 18 | "@formatjs/intl-relativetimeformat": "^3.0.1", 19 | "@scatterjs/core": "^2.7.44", 20 | "@scatterjs/eosjs2": "^1.5.26", 21 | "@scatterjs/lynx": "^1.6.37", 22 | "axios": "^0.19.0", 23 | "chart.js": "^2.8.0", 24 | "connect": "^3.6.6", 25 | "crypto-js": "^3.1.9-1", 26 | "eosjs": "^20.0.0", 27 | "file-saver": "^2.0.2", 28 | "idle-vue": "^2.0.5", 29 | "int64-buffer": "^0.99.1007", 30 | "json-pretty-html": "^1.1.6", 31 | "marked": "^0.6.2", 32 | "nested-object-assign": "^1.0.3", 33 | "sanitize-html": "^1.20.0", 34 | "turndown": "^5.0.3", 35 | "vconsole": "^3.3.0", 36 | "vue-chartjs": "^3.4.2", 37 | "vue-i18n": "^7.3.3", 38 | "vue2-medium-editor": "^1.1.6", 39 | "vuedraggable": "^2.19.2", 40 | "vuelidate": "^0.7.4", 41 | "vuex-persistedstate": "^2.5.4" 42 | }, 43 | "devDependencies": { 44 | "@vue/eslint-config-standard": "^4.0.0", 45 | "babel-core": "6", 46 | "babel-eslint": "^10.0.1", 47 | "copy-webpack-plugin": "^5.0.2", 48 | "eslint": "^5.16.0", 49 | "eslint-config-prettier": "^4.1.0", 50 | "eslint-loader": "^2.1.2", 51 | "eslint-plugin-prettier": "^3.0.1", 52 | "eslint-plugin-vue": "^5.2.2", 53 | "json-loader": "^0.5.7", 54 | "prettier": "^1.16.4", 55 | "quasar-cli": "^0.17.25", 56 | "strip-ansi": "=3.0.1" 57 | }, 58 | "engines": { 59 | "node": ">= 8.9.0", 60 | "npm": ">= 5.6.0", 61 | "yarn": ">= 1.6.0" 62 | }, 63 | "browserslist": [ 64 | "> 1%", 65 | "last 2 versions", 66 | "not ie <= 10" 67 | ] 68 | } 69 | -------------------------------------------------------------------------------- /src/index.template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <%= htmlWebpackPlugin.options.meta_title %> 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
33 | 34 | 35 | -------------------------------------------------------------------------------- /src/components/ui/period-timer.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 82 | -------------------------------------------------------------------------------- /src/components/ui/token-statistics.vue: -------------------------------------------------------------------------------- 1 | 36 | 37 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /src/components/controls/asset-input.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 92 | -------------------------------------------------------------------------------- /src/store/dac/getters.js: -------------------------------------------------------------------------------- 1 | //dac store 2 | export function getIsLoaded(state) { 3 | return state.isLoaded; 4 | } 5 | 6 | export function getMemberTerms(state) { 7 | if (!state.memberTerms) { 8 | console.log("memberterms not loaded yet"); 9 | return state.memberTerms; 10 | } 11 | return state.memberTerms; 12 | } 13 | 14 | export function getLatestMemberTerm(state) { 15 | if (!state.memberTerms) { 16 | console.log("memberterms not loaded yet"); 17 | return {}; 18 | } 19 | return state.memberTerms.slice(-1)[0]; 20 | } 21 | 22 | export function getDacAdmins(state) { 23 | return state.dacAdmins; 24 | } 25 | 26 | export function getCustodianPermissions(state) { 27 | return state.custodianPermissions; 28 | } 29 | 30 | export function getAuthAccountPermLevel(state, getters, rootState) { 31 | let level = "one"; 32 | if (state.dacAdmins.includes(rootState.user.accountName)) { 33 | level = "admin"; 34 | } 35 | return level; 36 | } 37 | 38 | export function getlatestTermsUrl(state) { 39 | return state.latestTermsUrl; 40 | } 41 | 42 | export function getCustodians(state) { 43 | if (!state.custodians) { 44 | console.log("custodians not loaded yet"); 45 | return []; 46 | } 47 | return state.custodians; 48 | } 49 | 50 | export function getCustodianConfig(state) { 51 | return state.custodianConfig; 52 | } 53 | 54 | export function getEnableCustPayments(state) { 55 | if ( 56 | state.custodianConfig.requested_pay_max === null || 57 | state.custodianConfig.requested_pay_max.contract === undefined 58 | ) { 59 | return false; 60 | } 61 | let req_pay_max_value = Number( 62 | state.custodianConfig.requested_pay_max.quantity.split(" ")[0] 63 | ); 64 | if (req_pay_max_value <= 0) { 65 | return false; 66 | } else { 67 | return true; 68 | } 69 | } 70 | 71 | export function getCustStakeConfig(state) { 72 | // let res = { 73 | // enabled: false, 74 | // symbol: null, 75 | // quantity: null, 76 | // contract: null 77 | // }; 78 | } 79 | 80 | export function getTokenStats(state) { 81 | return state.tokenStats; 82 | } 83 | 84 | export function getWpConfig(state) { 85 | return state.wpConfig; 86 | } 87 | 88 | export function getCandidates(state) { 89 | return state.candidates; 90 | } 91 | 92 | export function getCustodianState(state) { 93 | return state.custodianState; 94 | } 95 | 96 | export function getWpCategories(state) { 97 | return state.wpCategories; 98 | } 99 | -------------------------------------------------------------------------------- /src/css/themes/common.variables.styl: -------------------------------------------------------------------------------- 1 | 2 | 3 | @import './custom_theme_colors.styl'; 4 | 5 | // App Shared Variables 6 | // -------------------------------------------------- 7 | // To customize the look and feel of this app, you can override 8 | // the Stylus variables found in Quasar's source Stylus files. Setting 9 | // variables before Quasar's Stylus will use these variables rather than 10 | // Quasar's default Stylus variable values. Stylus variables specific 11 | // to the themes belong in either the variables.ios.styl or variables.mat.styl files. 12 | // Check documentation for full list of Quasar variables https://quasar-framework.org/components/stylus-variables.html 13 | $toolbar-min-height = 80px 14 | $item-highlight-color = rgba(255,255,255,0.1) 15 | $tabs-min-height = 70px 16 | 17 | // App Shared Color Variables 18 | // -------------------------------------------------- 19 | // It's highly recommended to change the default colors 20 | // to match your app's branding. 21 | // $primary = #4A1289 22 | // $secondary = #26A69A 23 | // $tertiary = #555 24 | 25 | // $neutral = #E0E1E2 26 | // $positive = #21BA45 27 | // $negative = #DB2828 28 | // $info = #31CCEC 29 | // $warning = #F2C037 30 | // $dark = #393D46 31 | // //default colors 32 | // $bg1 = #1E2128 33 | // $bg2 = #272B35 34 | // $text1 = rgba(255,255,255,0.9) 35 | // $text2 = rgba(255,255,255,0.7) 36 | 37 | 38 | $primary-light = lighten($primary, 20) 39 | 40 | 41 | 42 | :root 43 | --q-color-primary-light $primary-light 44 | --q-color-bg1 $bg1 45 | --q-color-bg2 $bg2 46 | --q-color-text1 $text1 47 | --q-color-text2 $text2 48 | 49 | 50 | .text-primary-light 51 | color $primary-light !important 52 | color var(--q-color-primary-light) !important 53 | .bg-primary-light 54 | background-color $primary-light !important 55 | background-color var(--q-color-primary-light) !important 56 | 57 | .text-bg1 58 | color $bg1 !important 59 | color var(--q-color-bg1) !important 60 | .bg-bg1 61 | background-color $bg1 !important 62 | background-color var(--q-color-bg1) !important 63 | 64 | .text-bg2 65 | color $bg2 !important 66 | color var(--q-color-bg2) !important 67 | .bg-bg2 68 | background-color $bg2 !important 69 | background-color var(--q-color-bg2) !important 70 | 71 | .text-text1 72 | color $text1 !important 73 | color var(--q-color-text1) !important 74 | .bg-text1 75 | background-color $text1 !important 76 | background-color var(--q-color-text1) !important 77 | 78 | .text-text2 79 | color $text2 !important 80 | color var(--q-color-text2) !important 81 | .bg-text2 82 | background-color $text2 !important 83 | background-color var(--q-color-text2) !important 84 | 85 | -------------------------------------------------------------------------------- /src/components/ui/auth-display.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 99 | -------------------------------------------------------------------------------- /src/components/ui/vote-animation.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 49 | 50 | 100 | -------------------------------------------------------------------------------- /src/components/controls/sleep-handler.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 96 | -------------------------------------------------------------------------------- /src/components/controls/firehose.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 89 | -------------------------------------------------------------------------------- /src/statics/images/eosdac_logo_notext.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/store/user/mutations.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | 3 | export function setIsLoaded(state, payload) { 4 | state.isLoaded = payload; 5 | } 6 | 7 | export function setAccountName(state, payload) { 8 | state.accountName = payload; 9 | } 10 | 11 | export function setProfilePicture(state, payload) { 12 | state.profilePicture = payload; 13 | } 14 | 15 | export function setSettingItem(state, payload) { 16 | // return state.settings; 17 | state.settings.find(s => payload.name == s.name).value = payload.value; 18 | } 19 | 20 | export function setDacBalance(state, payload) { 21 | state.dacBalance = payload; 22 | } 23 | 24 | export function setSystemBalance(state, payload) { 25 | state.systemBalance = payload; 26 | } 27 | 28 | export function setAccount(state, payload) { 29 | state.account = payload; 30 | } 31 | 32 | export function setAgreedTermsVersion(state, payload) { 33 | state.agreedTermsVersion = payload; 34 | } 35 | 36 | export function setLastTransaction(state, payload) { 37 | state.lastTransaction = payload; 38 | } 39 | 40 | export function setIsCandidate(state, payload) { 41 | state.isCandidate = payload; 42 | } 43 | 44 | export function setLanguage(state, payload) { 45 | state.language = payload; 46 | } 47 | 48 | export function setDacVotes(state, payload) { 49 | state.dacVotes = payload; 50 | } 51 | 52 | export function setCatDelegations(state, payload) { 53 | state.catDelegations = payload; 54 | } 55 | 56 | export const setMsigIsSeenCache = (state, payload) => { 57 | let mode = payload.mode; 58 | let msig_id = payload.msig_id; 59 | 60 | switch (mode) { 61 | case "add": 62 | if (state.msigIsSeenCache.indexOf(msig_id) === -1) 63 | state.msigIsSeenCache.push(msig_id); 64 | break; 65 | case "remove": 66 | state.msigIsSeenCache = state.msigIsSeenCache.filter(m => m !== msig_id); 67 | break; 68 | case "clear": 69 | state.msigIsSeenCache = []; 70 | break; 71 | default: 72 | // code block 73 | } 74 | }; 75 | 76 | export function setMsigTransferQeue(state, payload) { 77 | let mode = payload.mode; 78 | let qeue_entry = payload.qeue_entry; //can be index or entry object 79 | 80 | switch (mode) { 81 | case "add": 82 | let f = state.msigTransferQeue.findIndex(qi => qi.status == 3); 83 | if (f != -1) { 84 | console.log("edit item index", f); 85 | qeue_entry.status = 0; 86 | Vue.set(state.msigTransferQeue, f, qeue_entry); 87 | } else { 88 | state.msigTransferQeue.push(qeue_entry); 89 | } 90 | break; 91 | case "remove": 92 | state.msigTransferQeue.splice(qeue_entry, 1); 93 | break; 94 | case "clear": 95 | state.msigTransferQeue.splice(0, state.msigTransferQeue.length); 96 | break; 97 | default: 98 | // code block 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/components/ui/profile-pic.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 111 | -------------------------------------------------------------------------------- /src/components/controls/lang-selector.vue: -------------------------------------------------------------------------------- 1 | 57 | 58 | 105 | -------------------------------------------------------------------------------- /src/components/controls/network-switcher.vue: -------------------------------------------------------------------------------- 1 | 13 | 14 | 100 | 101 | 104 | -------------------------------------------------------------------------------- /src/components/ui/action-parser.vue: -------------------------------------------------------------------------------- 1 | 51 | 52 | 98 | 99 | 109 | -------------------------------------------------------------------------------- /src/store/user/getters.js: -------------------------------------------------------------------------------- 1 | export function getIsLoaded(state, payload) { 2 | return state.isLoaded; 3 | } 4 | 5 | export function getAccountName(state) { 6 | return state.accountName; 7 | } 8 | 9 | export function getAccount(state) { 10 | return state.account; 11 | } 12 | 13 | export function getAuthString(state) { 14 | if (state.account) { 15 | return `${state.account.name}@${state.account.authority}`; 16 | } 17 | } 18 | 19 | export function getAuth(state) { 20 | if (state.account) { 21 | return `${state.account.authority}`; 22 | } 23 | } 24 | 25 | export function getProfilePicture(state) { 26 | return state.profilePicture; 27 | } 28 | 29 | export function getDacBalance(state) { 30 | return state.dacBalance; 31 | } 32 | 33 | export function getSystemBalance(state) { 34 | return state.systemBalance; 35 | } 36 | 37 | export function getAgreedTermsVersion(state) { 38 | return state.agreedTermsVersion; 39 | } 40 | 41 | export function getSettings(state) { 42 | return state.settings; 43 | } 44 | 45 | export function getSettingByName(state) { 46 | return settingname => { 47 | return state.settings.find(s => settingname == s.name); 48 | }; 49 | } 50 | 51 | export function getMemberStatus(state, getters) { 52 | if (!getters.getAccountName || !getters.getAgreedTermsVersion) { 53 | return false; 54 | } 55 | let stake_balance = false; 56 | if (getters.getIsCandidate) { 57 | let locked_tokens = getters.getIsCandidate.locked_tokens; 58 | if (locked_tokens) { 59 | stake_balance = Number(locked_tokens.split(" ")[0]); 60 | } 61 | } 62 | 63 | if ( 64 | (getters.getDacBalance || stake_balance) && 65 | getters.getAgreedTermsVersion 66 | ) { 67 | return "member"; 68 | } else { 69 | return "pending"; 70 | } 71 | } 72 | 73 | export function getLastTransaction(state) { 74 | return state.lastTransaction; 75 | } 76 | 77 | export function getIsCandidate(state, payload) { 78 | return state.isCandidate; 79 | } 80 | 81 | export function getLanguage(state) { 82 | return state.language; 83 | } 84 | 85 | export function getDacVotes(state) { 86 | return state.dacVotes; 87 | } 88 | 89 | export function getCatDelegations(state) { 90 | return state.catDelegations; 91 | } 92 | 93 | export function getIsCustodian(state, getters, rootState) { 94 | const devs = ["piecesnbitss"]; 95 | if (rootState.dac.custodians && getters.getAccountName) { 96 | let res = rootState.dac.custodians.find( 97 | c => c.cust_name == getters.getAccountName 98 | ); 99 | if ( 100 | res || 101 | rootState.dac.dacAdmins.includes(state.accountName) || 102 | devs.includes(state.accountName) 103 | ) { 104 | return true; 105 | } else { 106 | return false; 107 | } 108 | } else { 109 | return false; 110 | } 111 | } 112 | 113 | export function getMsigIsSeenCache(state) { 114 | return state.msigIsSeenCache; 115 | } 116 | 117 | export function getMsigTransferQeue(state) { 118 | return state.msigTransferQeue; 119 | } 120 | -------------------------------------------------------------------------------- /src/components/ui/social-links.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 93 | 94 | 114 | -------------------------------------------------------------------------------- /src/plugins/helper.js: -------------------------------------------------------------------------------- 1 | class Helper { 2 | constructor(store) { 3 | this.store = store; 4 | } 5 | 6 | isUrl(url) { 7 | if (typeof url !== "string") { 8 | return false; 9 | } 10 | const re = /^(?:(?:https?|ftp):\/\/)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})))(?::\d{2,5})?(?:\/\S*)?$/; 11 | return re.test(url); 12 | } 13 | 14 | isEmail(email) { 15 | var re = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; 16 | return re.test(String(email).toLowerCase()); 17 | } 18 | 19 | noBackSlash(url) { 20 | return url.replace(/\/+$/, ""); 21 | } 22 | 23 | isAccountname(accountname) { 24 | const re = /^[a-z1-5.]{1,12}$/; 25 | return re.test(accountname); 26 | } 27 | 28 | toLocaleNumber(number, decimals = 4) { 29 | if (!number) return number; 30 | let test = this.store.getters["user/getSettingByName"]("locale_numbers") 31 | .value; 32 | if (test) { 33 | return parseFloat(number).toLocaleString(undefined, { 34 | minimumFractionDigits: decimals 35 | }); 36 | } else { 37 | return number; 38 | } 39 | } 40 | 41 | assetToLocaleNumber(asset) { 42 | if (!asset) return asset; 43 | let [quantity, symbol] = asset.split(" "); 44 | return `${this.toLocaleNumber(parseFloat(quantity))} ${symbol}`; 45 | } 46 | 47 | assetToNumber(asset) { 48 | if (asset) { 49 | return parseFloat(asset.split(" ")[0]); 50 | } 51 | } 52 | 53 | numberToAsset(num, symbol) { 54 | return `${num} ${symbol}`; 55 | } 56 | 57 | fullHostToJson(fullhost) { 58 | let [protocol, host, port] = fullhost.split(":"); 59 | 60 | let parts = { 61 | protocol: protocol, 62 | host: host.replace(/\//g, ""), 63 | port: port || (protocol === "https" ? "443" : "80") 64 | }; 65 | 66 | return parts; 67 | } 68 | 69 | randomIntFromInterval(min, max) { 70 | // min and max included 71 | return Math.floor(Math.random() * (max - min + 1) + min); 72 | } 73 | 74 | randomName() { 75 | let name = ""; 76 | let possible = "abcdefghijklmnopqrstuvwxyz12345."; 77 | for (let i = 0; i < 12; i++) { 78 | name += possible.charAt(Math.floor(Math.random() * possible.length)); 79 | } 80 | return name; 81 | } 82 | 83 | truncate(str, length) { 84 | let dots = str.length > length ? "..." : ""; 85 | return str.substring(0, length) + dots; 86 | } 87 | 88 | chunkArray(arr, chunksize) { 89 | let R = []; 90 | for (var i = 0; i < arr.length; i += chunksize) { 91 | R.push(arr.slice(i, i + chunksize)); 92 | } 93 | return R; 94 | } 95 | } 96 | 97 | export default ({ Vue, store }) => { 98 | Vue.prototype.$helper = new Helper(store); 99 | }; 100 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 107 | -------------------------------------------------------------------------------- /src/plugins/profile-cache.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | class ProfileCache { 4 | constructor(config) { 5 | console.log("profile cache initiated"); 6 | this.config = config; 7 | this.cache = []; 8 | this.default_avatar = "./branding/images/default-avatar.png"; 9 | let cache_life = 1000 * 60 * 60; 10 | 11 | if (cache_life) { 12 | setInterval(() => { 13 | this.cache = []; 14 | console.log("emptied cache"); 15 | }, cache_life); 16 | } 17 | } 18 | 19 | async getProfiles(accountnames, force_reload = false) { 20 | if (force_reload) this.removeFromCache(accountnames); 21 | //reduce requested accountnames 22 | let profiles = accountnames.reduce( 23 | (temp, accountname) => { 24 | let t = this.inCache(accountname); 25 | t ? temp.cached.push(t) : temp.fetch.push(accountname); 26 | return temp; 27 | }, 28 | { cached: [], fetch: [] } 29 | ); 30 | 31 | //fetch profiles only when not in cache 32 | if (profiles.fetch.length) { 33 | profiles.fetch = await this.fetchProfiles(profiles.fetch); 34 | //remove http images 35 | profiles.fetch.forEach(p => { 36 | if (p.profile.image.startsWith("http:")) { 37 | p.profile.image = ""; 38 | } 39 | }); 40 | } 41 | 42 | //return combined array of profiles 43 | return [...profiles.cached, ...profiles.fetch]; 44 | } 45 | 46 | async getAvatars(accountnames, force_reload = false) { 47 | let profiles = await this.getProfiles(accountnames, force_reload); 48 | let avatars = accountnames.map(accountname => { 49 | let p = profiles.find(x => x.account === accountname); 50 | let img = 51 | p && p.profile && p.profile.image 52 | ? p.profile.image 53 | : this.default_avatar; 54 | return { account: accountname, image: img }; 55 | }); 56 | return avatars; 57 | } 58 | 59 | async fetchProfiles(accountnames) { 60 | let url = this.config.get("memberclientstateapi"); 61 | // let dacname = "eosdac"; //this.config.get("dacname"); 62 | let params = { account: accountnames.join(",") }; 63 | const header = { "X-DAC-Name": this.config.get("dacscope") }; 64 | return axios({ 65 | method: "get", 66 | url: `${url}/profile`, 67 | params: params, 68 | headers: header 69 | }) 70 | .then(r => { 71 | console.log("fetched new profiles", r.data.results.length); 72 | let p = r.data.results; 73 | this.cache = this.cache.concat(p); 74 | return p; 75 | }) 76 | .catch(e => { 77 | console.log("error during api request.", e); 78 | return false; 79 | }); 80 | } 81 | 82 | inCache(accountname) { 83 | let profile = this.cache.find(p => p.account == accountname); 84 | if (profile) { 85 | return profile; 86 | } else { 87 | return false; 88 | } 89 | } 90 | 91 | removeFromCache(accountnames) { 92 | this.cache = this.cache.filter(p => !accountnames.includes(p.account)); 93 | } 94 | 95 | delete() { 96 | this.cache = []; 97 | console.log("Profile cache emptied"); 98 | } 99 | } 100 | 101 | export default ({ Vue, store }) => { 102 | Vue.prototype.$profiles = new ProfileCache(Vue.prototype.$configFile); 103 | }; 104 | -------------------------------------------------------------------------------- /src/components/controls/menus/main-menu.vue: -------------------------------------------------------------------------------- 1 | 79 | 80 | 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # eosDAC Client 2 | 3 | ![image](https://user-images.githubusercontent.com/44613132/55268864-1dc82380-528e-11e9-9715-454457b8506e.png) 4 | 5 | ## Purpose 6 | This is the member client where the DAC members will interface with the deployed smart contract code. The code is written in javascript using Vue.js and [Quasar Framework](https://quasar.dev). 7 | 8 | ## Pre-Requisites 9 | You want to be able to read javascript code and have a basic understanding of frontend web development. If you want to simply change the assets to your DAC's messaging, branding, and images then the process is not too difficult. 10 | 11 | [Original Design document](https://docs.google.com/document/d/1C4yzFNpK0Iz0Ru0gz28HeLJic5vZWBmVl3wV8czhVS4/edit#). 12 | 13 | ## Install 14 | 15 | [yarn](https://yarnpkg.com) is required to install 16 | 17 | ```bash 18 | $ git clone https://github.com/eosdac/eosdac-client.git 19 | $ cd eosdac-client 20 | $ yarn 21 | ``` 22 | 23 | ## Customize the eosdac-client 24 | 25 | Visit https://github.com/eosdac/boilerplate-client-extension for more info. 26 | 27 | Add custom colors, pages, routes and vuex stores. It's recommended to extend and customize the eosDAC client in the extensions folder so you can pull in future updates without losing your customizations. The boilerplate extension already contains a mandatatory home screen (eosDAC) and a custom example page with dedicated vuex store. You'll also find a component to extend the main menu. In production you might want to manage the contents of the extention folder as a git submodule. The command below will simply clone the remote repo in the src/extensions folder. 28 | 29 | ```bash 30 | cd eosdac-client 31 | yarn add_extensions https://github.com/eosdac/boilerplate-client-extension.git extensions 32 | ``` 33 | If you want a tighter coupling between the main repo and your extensions repo you can create a git submodule. 34 | ```bash 35 | cd eosdac-client 36 | git submodule add https://github.com/your_repo/your_dac-client-extension.git src/extensions 37 | ``` 38 | 39 | 40 | ## Quickstart 41 | 42 | Opens browser window automatically [http://localhost:8080](http://localhost:8080) 43 | 44 | ```bash 45 | yarn dev 46 | ``` 47 | 48 | ## Deployment 49 | 50 | Build the project for deployment. 51 | 52 | ```bash 53 | yarn build 54 | ``` 55 | 56 | You can run the deploy script to checkout the correct release tag and copy the dist/spa-mat files to a dist/deploy folder which you can then make the root folder of your nginx website. 57 | 58 | Example: 59 | 60 | ```bash 61 | $ deploy.sh v0.3.1 true 62 | ``` 63 | 64 | That will run tag v0.3.1 locally. 65 | 66 | ```bash 67 | $ deploy.sh v0.3.1 68 | ``` 69 | 70 | That will build v0.3.1 and copy the results to the dist/deploy folder. 71 | 72 | If you want to build and run specifically for the jungle tesnet, you can run `DEFAULT_NETWORK=jungle quasar build` 73 | 74 | ![image](https://user-images.githubusercontent.com/44613132/55268765-5b787c80-528d-11e9-8d6c-24f75526888b.png) 75 | 76 | ![image](https://user-images.githubusercontent.com/44613132/55268803-aa261680-528d-11e9-97bb-8d492bc5b3f1.png) 77 | 78 | ![image](https://user-images.githubusercontent.com/44613132/55268811-c75ae500-528d-11e9-8332-fd71ff648dc0.png) 79 | 80 | ![image](https://user-images.githubusercontent.com/44613132/59903601-ab389100-9401-11e9-942e-7bb437708416.png) 81 | 82 | ![image](https://user-images.githubusercontent.com/44613132/59903659-db802f80-9401-11e9-874f-6ba7c44fb000.png) 83 | 84 | ![image](https://user-images.githubusercontent.com/44613132/59903752-12564580-9402-11e9-8dd9-e225286b74c9.png) 85 | 86 | 87 | -------------------------------------------------------------------------------- /src/components/ui/voting-progress.vue: -------------------------------------------------------------------------------- 1 | 42 | 43 | 120 | 121 | 126 | -------------------------------------------------------------------------------- /src/components/controls/member-select.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 127 | 128 | 142 | -------------------------------------------------------------------------------- /src/components/controls/abi-viewer.vue: -------------------------------------------------------------------------------- 1 | 82 | 83 | -------------------------------------------------------------------------------- /src/components/ui/request-signature.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 114 | 115 | 132 | -------------------------------------------------------------------------------- /src/components/controls/file-input.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 113 | 114 | 138 | -------------------------------------------------------------------------------- /src/components/controls/table-parser.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /src/i18n/it/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "constitution": { 3 | "switch_contrast": "Cambia Contrasto" 4 | }, 5 | "default": { 6 | "constitution": "Costituzione", 7 | "constitution_has_been_updated": "La Costituzione è stata aggiornata. Si prega di firmare la Costituzione aggiornata per utilizzare il Cliente Membro.", 8 | "login": "Accesso", 9 | "logout": "Disconnettersi", 10 | "member_client": "Membro Cliente", 11 | "menu": "Menu", 12 | "sign_the_constitution": "Firma la Costituzione", 13 | "you_have_not_yet_registered": "Non sei ancora registrato come membro. Si prega di firmare la costituzione per utilizzare il Cliente Membro.", 14 | "your_token_balance": "Il tuo saldo di {tokenName}" 15 | }, 16 | "four_zero_four": { 17 | "go_back": "Torna indietro", 18 | "sorry_nothing_here": "Scusa, niente qui ..." 19 | }, 20 | "lang_selector": { 21 | "choose_your_language": "Scegli la tua LINGUA", 22 | "languages": { 23 | "chinese": "Cinese semplificato)", 24 | "english_gb": "Inglese (GB)", 25 | "english_us": "Inglese (Stati Uniti)", 26 | "french": "francese", 27 | "german": "Tedesco", 28 | "italian": "Italiano", 29 | "japanese": "Giapponese", 30 | "korean": "Coreano", 31 | "russian": "Russo", 32 | "spanish": "Spagnolo", 33 | "vietnamese": "Vietnamita" 34 | } 35 | }, 36 | "markdown_viewer": { 37 | "select_text_to_edit": "Highlight the text you want to edit." 38 | }, 39 | "menu": { 40 | "custodians": "custodi", 41 | "member_status": "Stato Membro", 42 | "profile": "Profilo", 43 | "register_as_candidate": "Register As Candidate", 44 | "settings": "impostazioni", 45 | "unregister_as_candidate": "Unregister As Candidate" 46 | }, 47 | "profile": { 48 | "bio": "Biografía", 49 | "cancel": "cancel", 50 | "download": "Scarica", 51 | "edit": "Modifica", 52 | "familyName": "Cognome", 53 | "female": "Femmina", 54 | "gender": "Genere", 55 | "givenName": "Nome", 56 | "male": "Maschio", 57 | "other": "Altro", 58 | "profile": "Profilo", 59 | "profile_picture_url": "Immagine del profilo Url", 60 | "save": "Salvare", 61 | "social_link": "Collegamento sociale", 62 | "social_profile_link": "Link al profilo sociale", 63 | "website": "Sito web" 64 | }, 65 | "settings": { 66 | "API": "API", 67 | "API_endpoint": "Endpoint API", 68 | "NOT_REGISTERED": "NON REGISTRATO", 69 | "REGISTERED": "REGISTRATO", 70 | "language_preference": "Lingua preferita", 71 | "member_status": "Stato membro", 72 | "register_as_member": "Registrati come membro", 73 | "register_now": "Registrati ora", 74 | "settings": "impostazioni", 75 | "transaction_popup_label": "Popup di transazione", 76 | "unregister_membership": "Annulla registrazione appartenenza", 77 | "user_preference": "Impostazioni utente" 78 | }, 79 | "transaction": { 80 | "cancel": "Annulla", 81 | "transaction": "Transazione", 82 | "transaction_successful": "Transazione avvenuta" 83 | }, 84 | "vote_custodians": { 85 | "description_main": "Il Consiglio di amministrazione custodisce le operazioni e gli affari del DAC, inclusi, a titolo esemplificativo ma non esaustivo, la governance e l'amministrazione delle attività e delle passività del DAC. I seguenti membri del DAC hanno acquisito parte dei loro token per presentare se stessi e i candidati per una posizione nel consiglio di amministrazione che dura 7 giorni. Ogni 7 giorni, i tuoi voti vengono ricalcolati per selezionare chi farà parte della prossima commissione di custodia. La votazione è importante! Per favore, vota spesso e resta coinvolto nel DAC per sapere chi sta fornendo valore.", 86 | "description_side": "Puoi votare fino a {maxvotes} candidati custodi alla volta. Si prega di selezionare i candidati che secondo voi porteranno valore al {dacname}.", 87 | "my_votes": "il mio voto", 88 | "rows_per_page": "Righe per pagina", 89 | "search": "ricerca", 90 | "submit_my_votes": "Presentare il mio voto" 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/components/ui/financial-account.vue: -------------------------------------------------------------------------------- 1 | 53 | 54 | 142 | -------------------------------------------------------------------------------- /src/components/controls/trigger-newperiod.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 130 | 131 | 148 | -------------------------------------------------------------------------------- /src/i18n/de/index.json: -------------------------------------------------------------------------------- 1 | { 2 | "default": { 3 | "constitution": "Verfassung", 4 | "constitution_has_been_updated": "The Verfassung wurde aktualisiert. Bitte unterschreiben Sie die Verfassung, um Mitglied zu bleiben.", 5 | "login": "Einloggen", 6 | "logout": "Ausloggen", 7 | "member_client": "Member-Client", 8 | "menu": "Menü", 9 | "sign_the_constitution": "Unterschreiben Sie die Verfassung", 10 | "you_have_not_yet_registered": "Sie sind noch nicht als Mitglied registriert. Bitte unterschreiben Sie die Verfassung, um Mitglied zu werden.", 11 | "your_token_balance": "Dein {tokenName} Guthaben" 12 | }, 13 | "four_zero_four": { 14 | "go_back": "zurück", 15 | "sorry_nothing_here": "Entschuldigung, nichts hier ..." 16 | }, 17 | "lang_selector": { 18 | "choose_your_language": "Wähle deine Sprache", 19 | "languages": { 20 | "chinese": "Vereinfachtes Chinesisch", 21 | "english_gb": "Englisch", 22 | "english_us": "Amerikanisches Englisch", 23 | "french": "Französisch", 24 | "german": "Deutsch", 25 | "italian": "Italienisch", 26 | "japanese": "Japanisch", 27 | "korean": "Koreanisch", 28 | "russian": "Russisch", 29 | "spanish": "Spanisch", 30 | "vietnamese": "Vietnamesisch" 31 | } 32 | }, 33 | "markdown_viewer": { 34 | "select_text_to_edit": "Markieren Sie den Text den Sie bearbeiten möchten." 35 | }, 36 | "menu": { 37 | "custodians": "Verwalter", 38 | "member_status": "Mitgliedsstatus", 39 | "profile": "Profil", 40 | "register_as_candidate": "Als Verwalter registrieren", 41 | "settings": "Einstellungen", 42 | "unregister_as_candidate": "Registrierung als Verwalter zurückziehen" 43 | }, 44 | "profile": { 45 | "cancel": "Abbrechen", 46 | "download": "Herunterladen", 47 | "edit": "Bearbeiten", 48 | "female": "Weiblich", 49 | "gender": "Geschlecht", 50 | "givenName": "Vorname", 51 | "male": "Männlich", 52 | "other": "Andere", 53 | "profile": "Profil", 54 | "profile_picture_url": "Profilbild URL", 55 | "save": "Speichern", 56 | "social_link": "Soziale Links", 57 | "social_profile_link": "URL zum Sozialen Profil", 58 | "website": "Webseite" 59 | }, 60 | "settings": { 61 | "API": "API", 62 | "API_endpoint": "API-Endpunkt", 63 | "NOT_REGISTERED": "NICHT REGISTRIERT", 64 | "REGISTERED": "REGISTRIERT", 65 | "language_preference": "Spracheinstellung", 66 | "member_status": "Mitgliedsstatus", 67 | "register_as_member": "Registrieren Sie sich als Mitglied", 68 | "register_now": "Jetzt registrieren", 69 | "settings": "Einstellungen", 70 | "transaction_popup_label": "Transaktionspopups", 71 | "unregister_membership": "Mitgliedschaft aufheben", 72 | "user_preference": "Benutzereinstellungen" 73 | }, 74 | "transaction": { 75 | "cancel": "Abbrechen", 76 | "transaction": "Transaktion", 77 | "transaction_successful": "Transaktion Erfolgreich" 78 | }, 79 | "vote_custodians": { 80 | "candidate_list": "Kandidatenliste", 81 | "description_main": "Die Depotbank verwaltet die Geschäfte und Angelegenheiten des DAC, einschließlich, aber nicht beschränkt auf, die Verwaltung und Verwaltung der Vermögenswerte und Verbindlichkeiten des DAC. Die folgenden DAC-Mitglieder haben einige ihrer Token zur Verfügung gestellt, um sich selbst und Kandidaten für eine Position auf der Depotbank, die für 7 Tage dauern, vorzulegen. Alle 7 Tage werden Ihre Stimmen neu berechnet, um auszuwählen, wer an der nächsten Depotbank teilnimmt. Abstimmung ist wichtig! Bitte wählen Sie häufig und bleiben Sie innerhalb des DAC engagiert, um zu wissen, wer einen Mehrwert bietet.", 82 | "description_side": "Sie können bis zu {maxvotes} Verwalter wählen. Bitte wählen Sie Kandidaten, von denen Sie glauben, dass sie einen Mehrwert für {dacname} erbringen.", 83 | "my_votes": "Meine Stimme", 84 | "rows_per_page": "Zeilen pro Seite", 85 | "search": "Suche", 86 | "submit_my_votes": "Stimme abgeben", 87 | "voting_progress": "Fortschritt Wahl" 88 | }, 89 | "votingprogress": { 90 | "activated_at": "{dacname} wird bei 15% aktiviert", 91 | "loading": "lade...", 92 | "refresh": "aktualisiere", 93 | "seconds": "Sekunden", 94 | "threshold_met": "Registrierungs-Schwelle von 15% erreicht. {dacname} ist aktiviert.", 95 | "update_in": "Aktualisierung in" 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/components/controls/menus/custodian-menu.vue: -------------------------------------------------------------------------------- 1 | 119 | 120 | 136 | -------------------------------------------------------------------------------- /src/components/ui/display-action.vue: -------------------------------------------------------------------------------- 1 | 96 | 97 | 121 | 122 | 125 | -------------------------------------------------------------------------------- /src/css/app.styl: -------------------------------------------------------------------------------- 1 | @import '~variables' 2 | @import '~src/extensions/branding/css/extended.styl'; 3 | 4 | .q-item-sublabel{ 5 | color: var(--q-color-text2) !important 6 | } 7 | .q-item-label{ 8 | color: var(--q-color-text1) !important 9 | } 10 | .q-if-label{ 11 | color: var(--q-color-text1) !important 12 | } 13 | 14 | .q-if-control, .q-collapsible-toggle-icon { 15 | color: var(--q-color-text2) !important; 16 | } 17 | 18 | .q-stepper-title{ 19 | color:var(--q-color-text1) !important; 20 | } 21 | .q-stepper-tab{ 22 | background: var(--q-color-bg2) 23 | } 24 | 25 | .router-link-active { 26 | background: var(--q-color-primary) !important; 27 | } 28 | 29 | .topbar.q-tabs-position-top .q-tabs-bar { 30 | top: 0; 31 | } 32 | 33 | .gradient-bg-primary{ 34 | // background-image: linear-gradient(to right, , ) !important; 35 | background: linear-gradient(to bottom right,var(--q-color-primary), var(--q-color-primary-light)); 36 | } 37 | 38 | .blur-details{ 39 | background rgba(255, 255, 255, 0.1); 40 | } 41 | 42 | a { 43 | text-decoration: none; 44 | color: var(--q-color-primary-light); 45 | transition: all .2s ease-in-out; 46 | } 47 | a:hover{ 48 | color: var(--q-color-text1); 49 | } 50 | 51 | .a2{ 52 | text-decoration: none; 53 | color: var(--q-color-text1); 54 | transition: all .2s ease-in-out; 55 | } 56 | .a2:hover{ 57 | color: var(--q-color-primary-light); 58 | } 59 | 60 | 61 | 62 | .profile_image{ 63 | // display: inline-block; 64 | border-radius: 50%; 65 | background-position: center center; 66 | background-size: cover; 67 | border-radius:50%; 68 | 69 | 70 | } 71 | 72 | 73 | 74 | /*these are the default quasar breakpoints 75 | use these when you need to change additional 76 | styles*/ 77 | 78 | /*xs 576px Extra small sized window*/ 79 | @media only screen and (max-width: 575px) { 80 | .profile_image_outer_wrap{ 81 | transform: scale(0.5); 82 | top:30px !important; 83 | left:-15px 84 | } 85 | .profile_header_bottom_row{ 86 | margin-left:16px !important; 87 | } 88 | .profile_header_top_row{ 89 | margin-left: 80px !important; 90 | } 91 | 92 | 93 | } 94 | 95 | /*sm 768px Small sized window*/ 96 | @media only screen and (max-width: 767px) { 97 | /*.wp_col{ 98 | border-left:0 !important; 99 | border-top:1px solid rgba(255,255,255,0.5); 100 | margin-bottom:15px !important; 101 | padding-top:20px !important; 102 | }*/ 103 | .index-title{ 104 | font-size:35px; 105 | 106 | } 107 | .index-description{ 108 | font-size:16px; 109 | } 110 | 111 | 112 | } 113 | /*md 992px Medium-sized window*/ 114 | @media only screen and (min-width: 768px) and (max-width: 991px) { 115 | 116 | } 117 | 118 | /*lg 1200px Large sized window*/ 119 | @media only screen and (min-width: 992px) and (max-width: 1199px) { 120 | 121 | } 122 | 123 | /*xl Infinite Extra large sized window*/ 124 | @media only screen and (min-width: 1200px) { 125 | 126 | 127 | } 128 | 129 | /*xl+*/ 130 | @media only screen and (min-width: 1500px) { 131 | 132 | } 133 | 134 | 135 | /* right bounce */ 136 | .fa-arrow-right { 137 | animation: bounceRight 2s infinite; 138 | width:26px; 139 | padding-left:12px 140 | } 141 | 142 | 143 | @keyframes bounceRight { 144 | 0%, 145 | 20%, 146 | 50%, 147 | 80%, 148 | 100% { 149 | -ms-transform: translateX(0); 150 | transform: translateX(0); 151 | } 152 | 40% { 153 | -ms-transform: translateX(-7px); 154 | transform: translateX(-7px); 155 | } 156 | 60% { 157 | -ms-transform: translateX(-4px); 158 | transform: translateX(-4px); 159 | } 160 | } 161 | /* /right bounce */ 162 | 163 | .fa-vibrate{ 164 | animation: vibrate 1s cubic-bezier(.36,.07,.19,.97) both infinite; 165 | transform: translate3d(0, 0, 0); 166 | backface-visibility: hidden; 167 | } 168 | .fa-vibrate-y { 169 | animation: vibrate 1s cubic-bezier(.36,.07,.19,.97) both infinite; 170 | transform: translate3d(0, 0, 0); 171 | backface-visibility: hidden; 172 | 173 | } 174 | 175 | @keyframes vibrate { 176 | 10%, 90% { 177 | transform: translate3d(0,-1px, 0); 178 | } 179 | 180 | 20%, 80% { 181 | transform: translate3d( 0,2px, 0); 182 | } 183 | 184 | 30%, 50%, 70% { 185 | transform: translate3d( 0,-2px, 0); 186 | } 187 | 188 | 40%, 60% { 189 | transform: translate3d(2px, 0, 0); 190 | } 191 | } -------------------------------------------------------------------------------- /src/pages/dev/dev-tools-home.vue: -------------------------------------------------------------------------------- 1 | 84 | 85 | 139 | -------------------------------------------------------------------------------- /src/layouts/dacLayout.vue: -------------------------------------------------------------------------------- 1 | 83 | 84 | 144 | -------------------------------------------------------------------------------- /src/modules/nodeselector.js: -------------------------------------------------------------------------------- 1 | const request = require("request"); 2 | 3 | class NodeSelector { 4 | constructor(nodes_api_url) { 5 | //configs 6 | this.nodes_api_url = nodes_api_url; 7 | this.benchmark_url = "/v1/chain/get_info"; 8 | this.exclude_nodes = ["eoscochain", "atticlab", "eospacex"]; //blacklist nodes 9 | } 10 | 11 | async get_fastest_node() { 12 | var self = this; 13 | //only get nodes if not already done 14 | if (this.nodelist == undefined) { 15 | try { 16 | await this.get_nodes({ https_only: true }); 17 | } catch (e) {} //no need to catch the error here 18 | } 19 | //return false if node api error 20 | if (!this.nodelist || !this.nodelist.length) { 21 | console.log("error getting node list from api server!"); 22 | return false; 23 | } 24 | 25 | return new Promise(async function(resolve, reject) { 26 | let flag = true; 27 | while (flag) { 28 | try { 29 | let winner = await self._start_race(); 30 | if (winner.ms != "error") { 31 | flag = false; 32 | resolve(winner); //valid winner 33 | } else { 34 | //node errored so exclude from next race 35 | self.nodelist = self.nodelist.filter(node => { 36 | return node != winner.node; 37 | }); 38 | } 39 | } catch (e) { 40 | console.log(e); 41 | } 42 | } 43 | }); 44 | } 45 | 46 | get_nodes(config = { https_only: false }) { 47 | var self = this; 48 | return new Promise(function(resolve, reject) { 49 | request( 50 | { 51 | url: self.nodes_api_url, 52 | json: true 53 | }, 54 | function(err, response, body) { 55 | if (err || response.statusCode !== 200) { 56 | self.nodelist = false; 57 | reject(false); 58 | } else { 59 | self.nodelist = body; 60 | if (config.https_only) { 61 | self.nodelist = body.filter(node => node.startsWith("https")); 62 | self.nodelist = self.nodelist.map(node => { 63 | node = node.substr(-1) == "/" ? node.slice(0, -1) : node; 64 | return node; 65 | }); 66 | if (self.exclude_nodes.length) { 67 | //filter out excluded nodes 68 | // console.log('node list', self.nodelist) 69 | self.nodelist = self.nodelist.filter(n => { 70 | for (var i = 0; i < self.exclude_nodes.length; i++) { 71 | if (n.indexOf(self.exclude_nodes[i]) > -1) { 72 | return false; 73 | } 74 | } 75 | return true; 76 | }); 77 | // console.log('node list excluded', self.nodelist) 78 | } 79 | } 80 | resolve(self.nodelist); 81 | } 82 | } 83 | ); 84 | }); 85 | } 86 | 87 | _start_race() { 88 | console.log(`start race with ${this.nodelist.length} nodes...`); 89 | if (this.nodelist.length < 1) { 90 | console.log("There are no nodes left to race!"); 91 | return false; 92 | } 93 | this.proms = []; 94 | this.nodelist.forEach((node, index) => { 95 | node = node.substr(-1) == "/" ? node.slice(0, -1) : node; 96 | let p = this._racer_request(node) 97 | .then(res => res) 98 | .catch(e => e); 99 | this.proms.push(p); 100 | }); 101 | return Promise.race(this.proms).then(function(winner) { 102 | return winner; 103 | }); 104 | } 105 | 106 | _racer_request(node_url) { 107 | var self = this; 108 | let url = node_url; 109 | return new Promise(function(resolve, reject) { 110 | request( 111 | { 112 | method: "POST", 113 | url: url + self.benchmark_url, 114 | time: true, 115 | rejectUnauthorized: false 116 | // headers: { 117 | // 'User-Agent': 'Chrome/59.0.3071.115', 118 | // 'Content-Type': 'text/plain;charset=UTF-8', 119 | // 'Access-Control-Request-Headers': 'Content-Type' 120 | // }, 121 | }, 122 | function(err, response) { 123 | if (err) { 124 | reject({ node: node_url, ms: "error" }); 125 | } else { 126 | resolve({ node: node_url, ms: response.elapsedTime }); 127 | } 128 | } 129 | ); 130 | }); 131 | } 132 | } //end class 133 | export default NodeSelector; 134 | /*module.exports = { 135 | NodeSelector 136 | };*/ 137 | -------------------------------------------------------------------------------- /src/components/ui/transaction-overlay.vue: -------------------------------------------------------------------------------- 1 | 102 | 103 | 150 | -------------------------------------------------------------------------------- /src/assets/icon-fonts/extended_material_icons/license.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Austin Andrews (http://materialdesignicons.com/), 2 | with Reserved Font Name Material Design Icons. 3 | 4 | Copyright (c) 2014, Google (http://www.google.com/design/) 5 | uses the license at https://github.com/google/material-design-icons/blob/master/LICENSE 6 | 7 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 8 | This license is copied below, and is also available with a FAQ at: 9 | http://scripts.sil.org/OFL 10 | 11 | 12 | ----------------------------------------------------------- 13 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 14 | ----------------------------------------------------------- 15 | 16 | PREAMBLE 17 | The goals of the Open Font License (OFL) are to stimulate worldwide 18 | development of collaborative font projects, to support the font creation 19 | efforts of academic and linguistic communities, and to provide a free and 20 | open framework in which fonts may be shared and improved in partnership 21 | with others. 22 | 23 | The OFL allows the licensed fonts to be used, studied, modified and 24 | redistributed freely as long as they are not sold by themselves. The 25 | fonts, including any derivative works, can be bundled, embedded, 26 | redistributed and/or sold with any software provided that any reserved 27 | names are not used by derivative works. The fonts and derivatives, 28 | however, cannot be released under any other type of license. The 29 | requirement for fonts to remain under this license does not apply 30 | to any document created using the fonts or their derivatives. 31 | 32 | DEFINITIONS 33 | "Font Software" refers to the set of files released by the Copyright 34 | Holder(s) under this license and clearly marked as such. This may 35 | include source files, build scripts and documentation. 36 | 37 | "Reserved Font Name" refers to any names specified as such after the 38 | copyright statement(s). 39 | 40 | "Original Version" refers to the collection of Font Software components as 41 | distributed by the Copyright Holder(s). 42 | 43 | "Modified Version" refers to any derivative made by adding to, deleting, 44 | or substituting -- in part or in whole -- any of the components of the 45 | Original Version, by changing formats or by porting the Font Software to a 46 | new environment. 47 | 48 | "Author" refers to any designer, engineer, programmer, technical 49 | writer or other person who contributed to the Font Software. 50 | 51 | PERMISSION & CONDITIONS 52 | Permission is hereby granted, free of charge, to any person obtaining 53 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 54 | redistribute, and sell modified and unmodified copies of the Font 55 | Software, subject to the following conditions: 56 | 57 | 1) Neither the Font Software nor any of its individual components, 58 | in Original or Modified Versions, may be sold by itself. 59 | 60 | 2) Original or Modified Versions of the Font Software may be bundled, 61 | redistributed and/or sold with any software, provided that each copy 62 | contains the above copyright notice and this license. These can be 63 | included either as stand-alone text files, human-readable headers or 64 | in the appropriate machine-readable metadata fields within text or 65 | binary files as long as those fields can be easily viewed by the user. 66 | 67 | 3) No Modified Version of the Font Software may use the Reserved Font 68 | Name(s) unless explicit written permission is granted by the corresponding 69 | Copyright Holder. This restriction only applies to the primary font name as 70 | presented to the users. 71 | 72 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 73 | Software shall not be used to promote, endorse or advertise any 74 | Modified Version, except to acknowledge the contribution(s) of the 75 | Copyright Holder(s) and the Author(s) or with their explicit written 76 | permission. 77 | 78 | 5) The Font Software, modified or unmodified, in part or in whole, 79 | must be distributed entirely under this license, and must not be 80 | distributed under any other license. The requirement for fonts to 81 | remain under this license does not apply to any document created 82 | using the Font Software. 83 | 84 | TERMINATION 85 | This license becomes null and void if any of the above conditions are 86 | not met. 87 | 88 | DISCLAIMER 89 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 90 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 91 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 92 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 93 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 94 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 95 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 96 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 97 | OTHER DEALINGS IN THE FONT SOFTWARE. -------------------------------------------------------------------------------- /src/modules/firehose-client.js: -------------------------------------------------------------------------------- 1 | const { Serialize } = require("eosjs"); 2 | const { TextDecoder, TextEncoder } = require("text-encoding"); 3 | const Int64 = require("int64-buffer").Int64BE; 4 | 5 | export class FirehoseClient { 6 | constructor(config, eosapi) { 7 | this.config = config; 8 | this.ready_cb = null; 9 | this.opened = false; 10 | 11 | this.Websocket = window.WebSocket || window.MozWebSocket; 12 | 13 | this.api = eosapi; 14 | this.connect(); 15 | } 16 | 17 | close() { 18 | console.log("closing firehose"); 19 | this.connection.close(); 20 | this.connection = null; 21 | } 22 | 23 | connect() { 24 | if (this.connection) { 25 | console.log("already connected"); 26 | return; 27 | } 28 | const connection = new this.Websocket(this.config.server); 29 | 30 | connection.onopen = () => { 31 | if (this.ready_cb) { 32 | this.ready_cb(this); 33 | } 34 | this.opened = true; 35 | // connection is opened and ready to use 36 | console.log("Firehose connected!"); 37 | }; 38 | 39 | connection.onerror = error => { 40 | // an error occurred when sending/receiving data 41 | console.error(error); 42 | }; 43 | 44 | connection.onmessage = message => { 45 | try { 46 | // console.log('onmessage', message) 47 | const event = JSON.parse(message.data); 48 | this.deserialize(event) 49 | .then(deserialized_event => { 50 | this.callback(event.type, deserialized_event); 51 | }) 52 | .catch(e => { 53 | console.error("Error onmessage: ", message.data, e); 54 | }); 55 | } catch (e) { 56 | console.error("Error onmessage: ", message.data, e); 57 | return; 58 | } 59 | }; 60 | 61 | this.connection = connection; 62 | } 63 | 64 | async getTableType(code, table) { 65 | const contract = await this.api.getContract(code); 66 | const abi = await this.api.getAbi(code); 67 | 68 | let this_table, type; 69 | for (let t of abi.tables) { 70 | if (t.name == table) { 71 | this_table = t; 72 | break; 73 | } 74 | } 75 | 76 | if (this_table) { 77 | type = this_table.type; 78 | } else { 79 | console.error(`Could not find table "${table}" in the abi`); 80 | return; 81 | } 82 | 83 | return contract.types.get(type); 84 | } 85 | 86 | arrayFromHex(hexString) { 87 | return new Uint8Array( 88 | hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16)) 89 | ); 90 | } 91 | 92 | async deserialize(raw) { 93 | // console.log('Deserialize', raw) 94 | 95 | let resp = null; 96 | const block_num = raw.block_num; 97 | 98 | switch (raw.type) { 99 | case "action_trace": 100 | let action = JSON.parse(raw.data); 101 | // console.log(action.data) 102 | let parsed_action = (await this.api.deserializeActions([action])).pop(); 103 | // console.log('Deserialize action_trace', raw, parsed_action) 104 | parsed_action.status = raw.status; 105 | resp = parsed_action; 106 | break; 107 | case "contract_row": 108 | const sb = new Serialize.SerialBuffer({ 109 | textEncoder: new TextEncoder(), 110 | textDecoder: new TextDecoder(), 111 | array: this.arrayFromHex(raw.data) 112 | }); 113 | 114 | sb.get(); // Row version 115 | const code = sb.getName(); 116 | const scope = sb.getName(); 117 | const table = sb.getName(); 118 | const primary_key = new Int64(sb.getUint8Array(8)).toString(); 119 | const payer = sb.getName(); 120 | const data_raw = sb.getBytes(); 121 | 122 | const table_type = await this.getTableType(code, table); 123 | const data_sb = new Serialize.SerialBuffer({ 124 | textEncoder: new TextEncoder(), 125 | textDecoder: new TextDecoder(), 126 | array: data_raw 127 | }); 128 | 129 | try { 130 | const data = table_type.deserialize(data_sb); 131 | resp = { 132 | block_num, 133 | code, 134 | scope, 135 | table, 136 | primary_key, 137 | payer, 138 | data 139 | }; 140 | } catch (e) { 141 | console.error("Error in deserialize", e); 142 | } 143 | break; 144 | case "fork": 145 | resp = raw; 146 | break; 147 | } 148 | 149 | resp.block_num = block_num; 150 | 151 | return resp; 152 | } 153 | 154 | ready(cb) { 155 | this.ready_cb = cb; 156 | 157 | if (this.opened) { 158 | cb(this); 159 | } 160 | 161 | return this; 162 | } 163 | 164 | callback(cb) { 165 | this.callback = cb; 166 | 167 | return this; 168 | } 169 | 170 | request(type, data) { 171 | const msg_obj = { type, data }; 172 | let msg = JSON.stringify(msg_obj); 173 | this.connection.send(msg); 174 | return this; 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/components/controls/color-picker.vue: -------------------------------------------------------------------------------- 1 | 73 | 74 | 180 | -------------------------------------------------------------------------------- /src/plugins/config-loader.js: -------------------------------------------------------------------------------- 1 | require("../assets/icon-fonts/eosdac-iconfont-v1-9/styles.css"); 2 | require("../assets/icon-fonts/extended_material_icons/css/materialdesignicons.css"); 3 | 4 | function styledConsoleLog() { 5 | let argArray = []; 6 | if (arguments.length) { 7 | const startTagRe = //gi; 8 | const endTagRe = /<\/span>/gi; 9 | let reResultArray; 10 | argArray.push( 11 | arguments[0].replace(startTagRe, "%c").replace(endTagRe, "%c") 12 | ); 13 | while ((reResultArray = startTagRe.exec(arguments[0]))) { 14 | argArray.push(reResultArray[2]); 15 | argArray.push(""); 16 | } 17 | for (var j = 1; j < arguments.length; j++) { 18 | argArray.push(arguments[j]); 19 | } 20 | } 21 | console.log.apply(console, argArray); 22 | } 23 | 24 | class ConfigLoader { 25 | constructor(networkname) { 26 | this.configFile = require(`../extensions/statics/config/config.${networkname}.json`); 27 | this.icon = require(`../extensions/statics/config/iconmap.json`); 28 | this.theme = require(`../extensions/statics/config/theme.json`); 29 | 30 | styledConsoleLog( 31 | `Welcome To the ${this.get( 32 | "dacname" 33 | )} Member Client Pasting code in the console can be dangerous ` 34 | ); 35 | } 36 | 37 | get(configquery) { 38 | switch (configquery) { 39 | case "theme_images": 40 | return this.theme.images; 41 | case "dacname": 42 | return this.configFile.dacName; 43 | case "dacscope": 44 | return this.configFile.dacScope.toLowerCase() || ""; 45 | case "defaultnode": 46 | return this.configFile.api.default_eos_node; 47 | case "tokencontract": 48 | return this.configFile.contracts.token.name; 49 | case "tokendecimals": 50 | return this.configFile.contracts.token.decimals; 51 | case "tokensupply": 52 | return this.configFile.contracts.token.totalSupply; 53 | case "marketapi": 54 | return this.configFile.contracts.token.market_api; 55 | case "custodianmemo": 56 | return this.configFile.contracts.custodian.memo; 57 | case "custodiancontract": 58 | return this.configFile.contracts.custodian.name; 59 | case "escrowcontract": 60 | return this.configFile.contracts.escrow.name; 61 | case "dactokensymbol": 62 | return this.configFile.contracts.token.symbol; 63 | case "systemtokensymbol": 64 | return this.configFile.contracts.system_token.symbol; 65 | case "systemtokendecimals": 66 | return this.configFile.contracts.system_token.decimals; 67 | case "systemtokencontract": 68 | return this.configFile.contracts.system_token.name; 69 | case "systemmsigcontract": 70 | return this.configFile.contracts.system_msig.name; 71 | case "dacmsigcontract": 72 | return this.configFile.contracts.dac_msig.name; 73 | case "botcontract": 74 | return this.configFile.contracts.bot.name; 75 | case "explorer": 76 | return this.configFile.external.explorer.replace(/\/+$/, ""); 77 | case "external": 78 | return this.configFile.external; 79 | case "memberclientstateapi": 80 | return this.configFile.api.memberclient_state_api.replace(/\/+$/, ""); 81 | case "bpnodeapi": 82 | return this.configFile.api.bpnodes; 83 | case "firehoseapi": 84 | return this.configFile.api.firehose; 85 | case "authaccount": 86 | return this.configFile.accounts.authAccount.name; 87 | case "wpcontract": 88 | return this.configFile.contracts.wpproposal.name; 89 | case "treasuryaccount": 90 | return this.configFile.accounts.treasuryAccount.name; 91 | case "financialaccounts": 92 | return this.configFile.financial_page_graphs; 93 | default: 94 | return `***${configquery} not yet subscribed in config-loader***`; 95 | } 96 | } 97 | 98 | setConfig(conf) { 99 | this.configFile = conf; 100 | } 101 | 102 | disable_ConsoleLog() { 103 | if (this.consoleLogBackup === undefined) { 104 | // alert('cl disabled') 105 | this.consoleLogBackup = window.console.log; 106 | window["console"]["log"] = function() {}; 107 | } 108 | } 109 | 110 | enable_ConsoleLog() { 111 | if (this.consoleLogBackup) { 112 | // alert('cl enable') 113 | window["console"]["log"] = this.consoleLogBackup; 114 | this.consoleLogBackup = undefined; 115 | } 116 | } 117 | } 118 | 119 | export default ({ Vue, store }) => { 120 | let networkname = store.getters["global/getActiveNetworkName"]; 121 | let config = new ConfigLoader(networkname); 122 | 123 | if ( 124 | store.getters["user/getSettingByName"]("debug_console_log") && 125 | !store.getters["user/getSettingByName"]("debug_console_log").value 126 | ) { 127 | config.disable_ConsoleLog(); 128 | } 129 | store.commit("global/setNode", config.get("defaultnode")); 130 | Vue.prototype.$configFile = config; 131 | }; 132 | -------------------------------------------------------------------------------- /src/router/routes.js: -------------------------------------------------------------------------------- 1 | import Guards from "./guards"; 2 | 3 | let routes_extension; 4 | try { 5 | routes_extension = require("../extensions/router/routes").default; 6 | } catch (e) { 7 | routes_extension = []; 8 | } 9 | 10 | let routes = [ 11 | { 12 | path: "/", 13 | component: () => import("layouts/dacLayout.vue"), 14 | children: [ 15 | { path: "", component: () => import("../extensions/pages/home") }, 16 | { 17 | path: "dashboard", 18 | component: () => import("pages/dashboard"), 19 | beforeEnter: Guards.logInCheck 20 | }, 21 | { path: "settings", component: () => import("pages/settings.vue") }, 22 | { 23 | path: "constitution", 24 | component: () => import("pages/constitution.vue") 25 | }, 26 | { 27 | path: "vote-custodians", 28 | component: () => import("pages/vote-custodians.vue") 29 | }, 30 | { 31 | path: "profile/:accountname", 32 | component: () => import("pages/profile") 33 | }, 34 | { 35 | path: "manage-candidateship", 36 | component: () => import("pages/manage-candidateship.vue") 37 | } 38 | ] 39 | }, 40 | 41 | { 42 | path: "/dev-tools", 43 | component: () => import("layouts/dacLayout.vue"), 44 | children: [ 45 | { path: "", component: () => import("../extensions/pages/home") }, 46 | { 47 | path: "memberclient", 48 | component: () => import("pages/dev/dev-tools-home") 49 | }, 50 | { path: "playground", component: () => import("pages/dev/playground") }, 51 | { 52 | path: "transactions", 53 | component: () => import("pages/dev/transactions") 54 | } 55 | ] 56 | }, 57 | 58 | { 59 | path: "/dac-activity", 60 | component: () => import("layouts/dacLayout.vue"), 61 | children: [ 62 | { path: "", component: () => import("../extensions/pages/home") }, 63 | { 64 | path: "financials", 65 | component: () => import("pages/custodian/dac-financials") 66 | }, 67 | { 68 | path: "review-msigs", 69 | component: () => import("pages/custodian/review-msigs") 70 | }, 71 | { 72 | path: "review-worker-proposals", 73 | component: () => import("pages/custodian/review-worker-proposals") 74 | }, 75 | { 76 | path: "explore-dac", 77 | component: () => import("pages/custodian/explore-dac") 78 | } 79 | ] 80 | }, 81 | 82 | { 83 | path: "/custodian", 84 | component: () => import("layouts/dacLayout.vue"), 85 | children: [ 86 | { path: "", component: () => import("../extensions/pages/home") }, 87 | { 88 | path: "review-msigs", 89 | component: () => import("pages/custodian/review-msigs") 90 | }, 91 | /* 92 | { 93 | path: "review-worker-proposals", 94 | component: () => import("pages/custodian/review-worker-proposals") 95 | }, 96 | */ 97 | { 98 | path: "my-payments", 99 | component: () => import("pages/custodian/my-payments"), 100 | beforeEnter: Guards.custodianCheck 101 | }, 102 | { 103 | path: "dac-management", 104 | component: () => import("pages/custodian/dac-management"), 105 | children: [ 106 | { path: "", redirect: "financials" }, 107 | { 108 | path: "financials", 109 | component: () => import("pages/custodian/dac-financials") 110 | }, 111 | { 112 | path: "configuration", 113 | component: () => import("pages/custodian/contracts-config") 114 | }, 115 | { 116 | path: "constitution", 117 | component: () => import("pages/custodian/manage-constitution") 118 | }, 119 | { 120 | path: "advanced", 121 | component: () => import("pages/custodian/advanced") 122 | } 123 | ] 124 | }, 125 | { 126 | path: "explore-dac", 127 | component: () => import("pages/custodian/explore-dac") 128 | } 129 | ] 130 | /* 131 | }, 132 | { 133 | path: "/member", 134 | component: () => import("layouts/dacLayout.vue"), 135 | children: [ 136 | { path: "", component: () => import("../extensions/pages/home") }, 137 | { 138 | path: "new-worker-proposal", 139 | component: () => import("pages/member/new-worker-proposal") 140 | }, 141 | { 142 | path: "my-worker-proposals", 143 | component: () => import("pages/member/my-worker-proposals") 144 | } 145 | ], 146 | beforeEnter: Guards.memberCheck 147 | */ 148 | } 149 | ]; 150 | 151 | //extend the default routes and overwrite when same paths 152 | routes_extension.forEach(re => { 153 | let existing_path = routes.find(r => r.path.trim() == re.path.trim()); 154 | if (existing_path) { 155 | re.children.forEach(rep => { 156 | let i = existing_path.children.findIndex( 157 | ep => ep.path.trim() == rep.path.trim() 158 | ); 159 | if (i === -1) { 160 | existing_path.children.push(rep); 161 | } else { 162 | existing_path.children[i] = rep; 163 | } 164 | }); 165 | } else { 166 | routes.push(re); 167 | } 168 | }); 169 | 170 | // Always leave this as last one 171 | if (process.env.MODE !== "ssr") { 172 | routes.push({ 173 | path: "*", 174 | component: () => import("pages/Error404.vue") 175 | }); 176 | } 177 | 178 | export default routes; 179 | --------------------------------------------------------------------------------