OSDN Git Service

update the bapp prompt function
[bytom/Byone.git] / src / views / prompts / bappPrompt.vue
1 <style lang="scss" scoped>
2   .warp {
3     position: absolute;
4     top: 0;
5     left: 0;
6     right: 0;
7     height: 600px;
8   }
9
10   .header {
11     display: flex;
12     justify-content: center;
13     font-weight: 600;
14     font-size: 28px;
15     color: rgba(0, 0, 0, 0.88);
16     margin-top: 60px;
17     margin-bottom: 25px;
18   }
19
20   .content {
21     padding: 20px;
22     overflow: scroll;
23     height: 160px;
24     background: #FAFAFA;
25     border: 1px solid #EBEBEB;
26     border-radius: 8px;
27     margin-bottom: 12px;
28     font-size: 14px;
29     word-break: break-all;
30   }
31
32   .content-black{
33     color: rgba(0, 0, 0, 0.64);
34   }
35
36   .tab-group {
37     font-size: 14px;
38     line-height: 140%;
39     letter-spacing: 0.2px;
40     display: flex;
41     margin-bottom: 12px;
42     a {
43       margin-right: 24px;
44     }
45   }
46
47   .divider {
48     margin: 12px 0;
49   }
50
51   .uint {
52     margin-left: 3px;
53   }
54
55   .btn-inline .btn {
56     margin: 10px 15px;
57   }
58
59   .row {
60     display: flex;
61     align-items: center;
62     .col {
63       display: flex;
64     }
65     .col:first-child {
66       width: 15%;
67     }
68
69     .col:last-child {
70       width: 85%;
71     }
72     .value {
73       display: flex;
74       font-size: 13px;
75       justify-content: space-between;
76     }
77
78     .wallet {
79       font-size: 14px;
80     }
81
82     .logo {
83       width: 26px;
84       height: 26px;
85       -webkit-border-radius: 16px;
86       -moz-border-radius: 16px;
87       border-radius: 16px;
88       background: white;
89       display: flex;
90       align-items: center;
91       justify-content: center;
92       font-size: 16px;
93       text-transform: uppercase;
94       color: black;
95       padding: 3px;
96     }
97   }
98
99   .form-item {
100     padding: 0;
101     margin: 0;
102     margin-bottom: 10px;
103   }
104
105   .hide {
106     width: 175px;
107     overflow: hidden;
108     text-overflow: ellipsis;
109     white-space: nowrap;
110   }
111
112   .view-link {
113     font-size: 14px;
114     color: #035BD4;
115     width: 275px;
116     display: block;
117   }
118
119   .iconfont.icon_arrow_2 {
120     transform: rotate(90deg);
121     color: #D6D6D6;
122     font-size: 24px;
123     margin-left: 4px;
124   }
125
126   .amount-list {
127     display: flex;
128     justify-content: space-between;
129     font-size: 14px;
130   }
131
132   .info {
133     margin-bottom: 20px;
134   }
135
136   pre{
137     margin: 0;
138     width: 100%;
139     word-break: break-all;
140     white-space: pre-wrap;
141     white-space: -moz-pre-wrap;
142     white-space: -pre-wrap;
143     white-space: -o-pre-wrap;
144     word-wrap: break-word;
145   }
146
147
148   .btn-round{
149     padding: 15px 2px;
150   }
151 </style>
152
153 <template>
154   <div>
155     <div class="warp">
156       <section class="header">
157         <h1 v-if="prompt.data && prompt.data.type ==='signTransaction'" >{{ $t('transfer.signComfirm') }}</h1>
158         <h1 v-else-if="prompt.data && prompt.data.type ==='message'" >{{ $t('transfer.signMessage') }}</h1>
159         <h1 v-else>{{ $t('transfer.confirmTransaction') }}</h1>
160       </section>
161
162       <section v-if="dataReady">
163         <div class="tab-group">
164           <a :class="{'color-black font-bold': tab === 'details'}" v-on:click="tab = 'details'">{{ $t('transfer.detail')
165             }}</a>
166           <a :class="{'color-black font-bold': tab === 'inout'}" v-on:click="tab = 'inout'">{{ $t('transfer.requestDetail')
167             }}</a>
168         </div>
169
170         <div v-if="tab ==='details'" class="content">
171           <!--Info-->
172           <div class="info">
173             <div class="row">
174               <div class="col">
175                 <img class="logo" src="@/assets/logo.png" alt="">
176               </div>
177               <div class="col value">
178                 <div v-if="currentWallet && currentWallet.alias" class="wallet color-black">{{currentWallet.alias}}</div>
179                 <div>{{shortAddress}}</div>
180               </div>
181             </div>
182             <div class="row">
183               <div class="col">
184                 <i class="iconfont icon_arrow_2"></i>
185               </div>
186               <div class="col divider"></div>
187             </div>
188
189             <div class="row">
190               <div class="col">
191                 <img v-if="prompt.domain && domainsMeta[prompt.domain] && domainsMeta[prompt.domain].icon" class="logo"
192                      :src="domainsMeta[prompt.domain].icon" alt="">
193                 <div v-else-if="initial" class="logo">
194                   {{initial}}
195                 </div>
196               </div>
197
198               <div class="col value">
199                 <span class="color-black"
200                       v-if="prompt.domain && domainsMeta[prompt.domain] && domainsMeta[prompt.domain].title ">{{domainsMeta[prompt.domain].title }}</span>
201                 <span class="color-black" v-else-if="prompt.domain ">{{prompt.domain }}</span>
202
203                 <div v-if="transaction.to">{{short(transaction.to)}}</div>
204               </div>
205             </div>
206           </div>
207
208           <!--transaction types-->
209           <div v-if="prompt.data && prompt.data.type ==='signTransaction'" class="amount-list">
210             <div >{{ $t('transfer.types') }}</div>
211             <div class="color-black font-bold">{{transaction.types}}</div>
212           </div>
213
214           <!--sign message-->
215           <div v-if="prompt.data && prompt.data.type ==='message'" class="amount-list">
216             <div style="min-width: 78px;" >{{ $t('signMessage.message') }}</div>
217             <div class="color-black font-bold">{{transaction.message}}</div>
218           </div>
219
220           <!--amounts-->
221           <div v-if="prompt.data && prompt.data.type ==='transfer'" class="amount-list">
222             <div>{{ $t('transfer.amount') }}</div>
223             <div class="color-black font-bold">{{transaction.amount}}<span class="uint uppercase">{{unit || short(transaction.asset) }}</span>
224             </div>
225           </div>
226           <div v-else v-for="(amountInput, index) in transaction.amounts" :key="index" class="amount-list">
227             <div>{{index ==0 && $t('transfer.amount') }}</div>
228             <div class="color-black font-bold">{{amountInput.amount}}<span class="uint uppercase">{{amountInput.alias || short(amountInput.asset) }}</span>
229             </div>
230           </div>
231
232           <!--fee-->
233           <div v-if="transaction.fee" class="amount-list">
234             <div>{{ $t('transfer.fee') }}</div>
235             <div class="color-black font-bold">{{transaction.fee}}<span class="uint">BTM</span></div>
236           </div>
237
238         </div>
239         <div v-if="tab ==='inout'" class="content content-black">
240           <pre>{{detail}}</pre>
241         </div>
242
243         <div class="form">
244           <div class="form-item">
245             <div class="form-item-content">
246               <input type="password" :placeholder="$t('transfer.password')" v-model="password" ref="password" autofocus>
247             </div>
248           </div>
249         </div>
250         <div>
251           <div class="btn btn-primary btn-round float-right" @click="transfer"><i class="iconfont icon-right-arrow"></i></div>
252         </div>
253
254       </section>
255       <section v-else>
256         loading...
257
258       </section>
259
260     </div>
261     <Footer/>
262   </div>
263 </template>
264
265 <script>
266   import transaction from "@/models/transaction";
267   import getLang from "@/assets/language/sdk";
268   import {apis} from '@/utils/BrowserApis';
269   import NotificationService from '../../services/NotificationService'
270   import {mapActions, mapGetters, mapState} from 'vuex'
271   import _ from 'lodash';
272   import account from "@/models/account";
273   import add from "@/utils/address";
274   import { camelize, removeFromArray } from "@/utils/utils";
275   import bytomjslib from 'bytomjs-lib'
276
277
278   export default {
279     add,
280     data() {
281       return {
282         dataReady: false,
283         transaction: {
284           confirmations: 1,
285         },
286         unit:null,
287         password: '',
288         prompt: '',
289         tab: 'details'
290       };
291     },
292     computed: {
293       initial() {
294         if (this.prompt && this.prompt.data && this.prompt.data.title) {
295           return this.prompt.data.title.substring(0, 1)
296         } else if (this.prompt && this.prompt.domain) {
297           return this.prompt.domain.substring(0, 1)
298         }
299       },
300       address: function () {
301         if (this.netType === 'vapor') {
302           return this.currentAccount.vpAddress
303         } else {
304           return this.currentAccount.address
305         }
306       },
307       shortAddress: function () {
308         if(this.prompt.data && this.prompt.data.type ==='transfer') {
309           return add.short(this.transaction.from);
310         }else if(this.prompt.data && this.prompt.data.type ==='message'){
311           return add.short(this.transaction.address);
312         }else{
313           if (this.netType === 'vapor') {
314             return add.short(this.currentAccount.vpAddress);
315           } else {
316             return add.short(this.currentAccount.address);
317           }
318         }
319       },
320       detail(){
321         if(this.prompt.data && this.prompt.data.type ==='signTransaction'){
322           const param = this.prompt.data[0] || this.prompt.data
323           const _tx = camelize(param)
324           const { rawTransaction, signingInstructions } = _tx
325           const obj = {
326             tx: this.transaction,
327             rawTransaction,
328             signingInstructions
329           }
330           return JSON.stringify(obj, null, 2);
331         }else if(this.prompt.data && this.prompt.data.type ==='message'){
332           const {confirmations, ...Attr} = this.transaction
333           return JSON.stringify(Attr, null, 2);
334         }else{
335           return JSON.stringify(this.transaction, null, 2);
336         }
337       },
338       currentWallet(){
339         if(this.prompt.data && this.prompt.data.type ==='transfer'){
340           return this.bytom.keychain.findByAddress(this.transaction.from);
341         }else if(this.prompt.data && this.prompt.data.type ==='message'){
342           return this.bytom.keychain.findByAddress(this.transaction.address);
343         }else{
344           return this.currentAccount
345         }
346       },
347       ...mapState([
348         'bytom'
349       ]),
350       ...mapGetters([
351         'currentAccount',
352         'net',
353         'netType',
354         'domainsMeta'
355       ])
356     },
357     watch: {},
358     methods: {
359       short(address){
360         return add.short(address)
361       },
362       transfer: function () {
363         if(!this.password){
364           this.$toast.error(
365             this.$t('error.BTM0008')
366           );
367           this.$refs.password.focus();
368           return;
369         }
370         let loader = this.$loading.show({
371           // Optional parameters
372           container: null,
373           canCancel: true,
374           onCancel: this.onCancel
375         });
376
377         if(this.prompt.data.type ==='advTransfer'){
378           transaction.buildTransaction(this.address, this.transaction.input, this.transaction.output, this.transaction.fee, this.transaction.confirmations).then(async (result) => {
379
380             let arrayData
381             if (this.transaction.args) {
382               arrayData = await transaction.convertArgument(this.transaction.args)
383             }
384
385             return transaction.advancedTransfer(this.address, result[0], this.password, arrayData, this)
386               .then((resp) => {
387                 loader.hide();
388                 this.prompt.responder(resp);
389                 this.$toast.success(
390                   this.$t("transfer.success")
391                 );
392                 NotificationService.close();
393               })
394               .catch(error => {
395                 throw error
396               });
397           }).catch(error => {
398             loader.hide();
399
400             this.$toast.error(
401               getLang(error.message) || error.message || error
402             );
403           });
404         } else if(this.prompt.data.type ==='transfer'){
405           transaction.transfer(this.transaction, this.password, this.address, this).then(result => {
406             loader.hide();
407             this.prompt.responder(result);
408             this.$toast.success(
409               this.$t("transfer.success")
410             );
411             NotificationService.close();
412
413           }).catch(error => {
414             loader.hide();
415             this.$toast.error(
416               getLang(error.message) || error.message || error
417             );
418           });
419         } else if(this.prompt.data.type ==='signTransaction'){
420           const data = this.prompt.data.value
421
422           if(Array.isArray(data)){
423             Promise.all(data.map( (rawdata) => transaction.signTransaction(this.address, rawdata, this.password, this)))
424               .then( (result) => {
425                 loader.hide();
426                 this.prompt.responder(result);
427                 this.$toast.success(this.$t("transfer.success"));
428                 NotificationService.close();
429               }).catch(error => {
430               loader.hide();
431
432               this.$toast.error(
433                 getLang(error.message) || error.message || error
434               );
435             });
436           }else{
437             transaction.signTransaction(this.address,  data,  this.password, this).then( (result) => {
438               loader.hide();
439               this.prompt.responder(result);
440               this.$toast.success(this.$t("transfer.success"));
441
442               NotificationService.close();
443             }).catch(error => {
444               loader.hide();
445
446               this.$toast.error(
447                 getLang(error.message) || error.message || error
448               );
449             });
450           }
451
452         } else if(this.prompt.data.type ==='message'){
453           transaction.signMessage(this.transaction.message, this.password, this.address, this).then((resp) => {
454             loader.hide();
455             this.prompt.responder(resp);
456             this.$toast.success(
457               this.$t("transfer.success")
458             );
459             NotificationService.close();
460           }).catch(error => {
461             loader.hide();
462
463             this.$toast.error( getLang(error.message));
464           });
465         }
466         else{
467           loader.hide();
468           this.$toast.error(
469             'Unknown popup type'
470           );
471         }
472
473       },
474       queryAsset: function (assetID) {
475         return transaction.asset(assetID)
476       }
477     }, mounted() {
478       this.prompt = window.data || apis.extension.getBackgroundPage().notification || null;
479       console.log(this.prompt)
480
481       const params = this.prompt.data
482       if (params !== undefined) {
483         const data = params.value
484         switch(params.type){
485           case "advTransfer":{
486             const inout = data
487             if (inout.input !== undefined) {
488               this.transaction.input = inout.input
489             }
490             if (inout.output !== undefined) {
491               this.transaction.output = inout.output
492             }
493             if (inout.args !== undefined) {
494               this.transaction.args = inout.args
495             }
496             if (inout.gas !== undefined) {
497               this.transaction.fee = inout.gas
498             }
499             if (inout.confirmations !== undefined) {
500               this.transaction.confirmations = inout.confirmations
501             }
502
503             const array = inout.input.filter(action => action.type === 'spend_wallet')
504
505             if (array.length > 0) {
506               account.setupNet(`${this.net}${this.netType}`)
507               const promise =
508                 _(array)
509                   .groupBy('asset')
510                   .map((objs, key) => {
511                     return this.queryAsset(key).then(resp => {
512                       return {
513                         'asset': key,
514                         'alias': resp.symbol,
515                         'amount': _.sumBy(objs, 'amount')
516                       }
517                     })
518                   })
519
520               let that = this;
521               Promise.all(promise).then(function (output) {
522                 that.transaction.amounts = output
523                 this.dataReady = true
524               }).catch(()=>{
525                 this.dataReady = true
526               })
527
528             }
529             break;
530           }
531           case "transfer":{
532             if (data.from != undefined) {
533               this.transaction.from = data.from
534             }
535             if (data.asset != undefined) {
536               this.transaction.asset=  data.asset
537             }
538             if (data.to != undefined) {
539               this.transaction.to = data.to
540             }
541             if (data.amount != undefined) {
542               this.transaction.amount = data.amount
543             }
544
545             if(data.confirmations != undefined) {
546               this.transaction.confirmations = data.confirmations
547             }
548
549             const asset_amounts ={}
550             asset_amounts[data.asset] = data.amount
551
552             transaction.estimateFee( data.from, asset_amounts).then( (resp) =>{
553               this.transaction.fee = resp.fee
554               this.queryAsset(data.asset).then(resp => {
555                 this.unit =  resp.symbol
556                 this.dataReady = true
557               }).catch((e)=>{
558                 throw e
559               })
560
561             }).catch(()=>{
562               this.dataReady = true
563             })
564             break;
565           }
566           case "signTransaction":{
567             const param = data[0] || data
568             const _tx = camelize(param)
569             const rawTransaction = _tx.rawTransaction
570
571             const tx = this.netType === 'vapor'?
572               bytomjslib.vapor.Transaction.decodeRawTransaction(rawTransaction):
573               bytomjslib.bytom.Transaction.decodeRawTransaction(rawTransaction)
574
575             this.transaction.fee = tx.fee/100000000
576             this.transaction.input = tx.inputs
577             this.transaction.output = tx.outputs
578
579             const inputs = tx.inputs.filter(i => i.address === this.address)
580             const outputs = tx.outputs.filter(i => i.address === this.address)
581             const inputAsset = inputs.map(i => i.assetID);
582             const outputAsset = outputs.map(i => i.assetID);
583
584             const asset = _.union(inputAsset, outputAsset)
585
586             let types = ["transfer"]
587             const promise =
588               asset
589                 .map((assetId) => {
590                   return this.queryAsset(assetId).then(resp =>{
591                     const assetInput = inputs.filter(i => i.assetID ===assetId)
592                     const assetOutput = outputs.filter(o => o.assetID ===assetId)
593                     const inputAmount = new BigNumber(_.sumBy(assetInput, 'amount'))
594                     const outputAmount = new BigNumber(_.sumBy(assetOutput, 'amount'))
595
596                     const decimals = decimalsMap[this.net][assetId]
597                     const amount = inputAmount.minus(outputAmount).shiftedBy(-decimals)
598
599                     return {
600                       'asset': assetId,
601                       'alias': resp.symbol,
602                       'amount': amount.toString()
603                     }
604                   })
605                 })
606
607             let that = this;
608             const inputType = inputs.map(i => i.type);
609             const outputType = outputs.map(o => o.type);
610             types = _.union(inputType, outputType, types);
611
612             const remove = ['spend','control'];
613             types = removeFromArray(types, remove);
614             types = types.map(ty => this.$t(`common.${ty}`)).join(', ');
615
616             this.transaction.types = types
617
618             Promise.all(promise).then(function(output) {
619               that.transaction.amounts = output
620               that.dataReady = true
621             }).catch(()=>{
622               that.dataReady = true
623             })
624
625
626             break
627           }
628           case "message":{
629             if(data.address !== undefined){
630               this.transaction.address = data.address;
631             }
632             if(data.message !== undefined){
633               this.transaction.message = data.message
634             }
635
636             this.dataReady = true
637
638             break
639           }
640         }
641       }
642     }
643   };
644 </script>