فهرست منبع

Merge branch 'test' of DeNet/de-net into master

zhangwei 3 سال پیش
والد
کامیت
44fb902f4d
59فایلهای تغییر یافته به همراه2535 افزوده شده و 1145 حذف شده
  1. 3 0
      src/assets/subject/001-back-head-top-1.svg
  2. 8 0
      src/assets/subject/001-icon-rabbit.svg
  3. 6 0
      src/assets/subject/001-icon-red-packet.svg
  4. 3 0
      src/assets/svg/icon-discord-mini.svg
  5. 1 0
      src/assets/svg/icon-discord.svg
  6. 0 6
      src/assets/svg/icon-follow.svg
  7. 3 0
      src/assets/svg/icon-home-bill.svg
  8. 3 0
      src/assets/svg/icon-home-wallet.svg
  9. 2 2
      src/assets/svg/icon-like.svg
  10. 3 0
      src/assets/svg/icon-loading-gray.svg
  11. 8 0
      src/assets/svg/icon-rabbit.svg
  12. 2 2
      src/assets/svg/icon-retweet.svg
  13. 3 0
      src/assets/svg/icon-telegram.svg
  14. 16 0
      src/assets/svg/icon-winner-more.svg
  15. 4 0
      src/assets/svg/icon-withdraw-help.svg
  16. 58 129
      src/entry/background.js
  17. 22 64
      src/entry/content.js
  18. 2 1
      src/entry/popup.js
  19. 2 1
      src/http/configAPI.js
  20. 37 0
      src/http/discordApi.js
  21. 18 0
      src/http/messageApi.js
  22. 19 10
      src/http/publishApi.js
  23. 9 1
      src/http/redPacket.js
  24. 0 1
      src/http/request.js
  25. 1 1
      src/iframe/home.js
  26. 1 1
      src/iframe/publish.js
  27. 1 1
      src/iframe/red-packet.js
  28. 2 0
      src/log-center/logEnum.js
  29. 188 0
      src/logic/background/fetch/twitter.js
  30. 319 0
      src/logic/background/twitter.js
  31. 212 0
      src/logic/content/ParseCard.js
  32. 187 364
      src/logic/content/twitter.js
  33. 9 4
      src/manifest.json
  34. 0 4
      src/rules/rules_1.json
  35. 0 76
      src/server/twitter.js
  36. 1 1
      src/uilts/chromeExtension.js
  37. 1 1
      src/view/components/message-box.vue
  38. 54 1
      src/view/components/popup-transactions.vue
  39. 1 1
      src/view/components/popup-withdraw.vue
  40. 26 0
      src/view/components/red-dot.vue
  41. 1 1
      src/view/iframe/home/home.vue
  42. 3 3
      src/view/iframe/publish/components/follow-input.vue
  43. 51 0
      src/view/iframe/publish/components/get-more.vue
  44. 2 1
      src/view/iframe/publish/components/paypal-button.vue
  45. 7 7
      src/view/iframe/publish/components/preview-card.vue
  46. 0 0
      src/view/iframe/publish/components/top-up.vue
  47. 358 86
      src/view/iframe/publish/give-dialog.vue
  48. 2 4
      src/view/iframe/publish/publish.vue
  49. 405 243
      src/view/iframe/red-packet/red-packet.vue
  50. 1 1
      src/view/options.vue
  51. 52 23
      src/view/popup/components/head.vue
  52. 0 0
      src/view/popup/components/login.vue
  53. 122 0
      src/view/popup/components/modal.vue
  54. 272 94
      src/view/popup/popup.vue
  55. 1 1
      src/view/popup/top-up/home.vue
  56. 2 2
      src/view/popup/transactions.vue
  57. 1 1
      src/view/popup/withdraw/home.vue
  58. 18 4
      src/view/popup/withdraw/info.vue
  59. 2 2
      src/view/popup/withdraw/paypal.vue

+ 3 - 0
src/assets/subject/001-back-head-top-1.svg

@@ -0,0 +1,3 @@
+<svg width="375" height="110" viewBox="0 0 375 110" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M0 0H375V85.8417C375 85.8417 320.624 110 187.5 110C54.376 110 0 85.8417 0 85.8417V0Z" fill="#1D9BF0"/>
+</svg>

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 8 - 0
src/assets/subject/001-icon-rabbit.svg


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 6 - 0
src/assets/subject/001-icon-red-packet.svg


+ 3 - 0
src/assets/svg/icon-discord-mini.svg

@@ -0,0 +1,3 @@
+<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M17.8611 4.99128C16.5992 4.41222 15.2459 3.9856 13.8309 3.74126C13.8052 3.73654 13.7794 3.74833 13.7661 3.7719C13.5921 4.08145 13.3993 4.48529 13.2643 4.8027C11.7424 4.57486 10.2284 4.57486 8.73772 4.8027C8.60269 4.47823 8.40291 4.08145 8.22808 3.7719C8.21481 3.74911 8.18907 3.73733 8.1633 3.74126C6.74913 3.98482 5.39585 4.41144 4.13308 4.99128C4.12215 4.99599 4.11278 5.00385 4.10656 5.01406C1.53966 8.84896 0.836475 12.5896 1.18143 16.2839C1.18299 16.3019 1.19314 16.3192 1.20719 16.3302C2.90076 17.5739 4.54127 18.329 6.15131 18.8294C6.17708 18.8373 6.20438 18.8279 6.22078 18.8067C6.60164 18.2866 6.94114 17.7382 7.23223 17.1614C7.24941 17.1277 7.23301 17.0876 7.1979 17.0742C6.65939 16.87 6.14663 16.6209 5.65339 16.3381C5.61437 16.3153 5.61125 16.2595 5.64714 16.2328C5.75094 16.155 5.85476 16.0741 5.95387 15.9924C5.9718 15.9774 5.99679 15.9743 6.01787 15.9837C9.25827 17.4632 12.7664 17.4632 15.9685 15.9837C15.9896 15.9735 16.0146 15.9767 16.0333 15.9916C16.1325 16.0733 16.2363 16.155 16.3408 16.2328C16.3767 16.2595 16.3744 16.3153 16.3354 16.3381C15.8421 16.6264 15.3294 16.87 14.7901 17.0735C14.755 17.0868 14.7394 17.1277 14.7565 17.1614C15.0539 17.7373 15.3934 18.2858 15.7672 18.8059C15.7828 18.8279 15.8109 18.8373 15.8367 18.8294C17.4545 18.329 19.095 17.5739 20.7886 16.3302C20.8034 16.3192 20.8128 16.3027 20.8144 16.2846C21.2272 12.0137 20.1229 8.3037 17.8869 5.01484C17.8814 5.00385 17.8721 4.99599 17.8611 4.99128ZM7.71613 14.0344C6.74055 14.0344 5.93669 13.1388 5.93669 12.0388C5.93669 10.9389 6.72496 10.0432 7.71613 10.0432C8.71508 10.0432 9.51115 10.9467 9.49553 12.0388C9.49553 13.1388 8.70727 14.0344 7.71613 14.0344ZM14.2953 14.0344C13.3197 14.0344 12.5159 13.1388 12.5159 12.0388C12.5159 10.9389 13.3041 10.0432 14.2953 10.0432C15.2943 10.0432 16.0903 10.9467 16.0747 12.0388C16.0747 13.1388 15.2943 14.0344 14.2953 14.0344Z" fill="#5865F2"/>
+</svg>

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1 - 0
src/assets/svg/icon-discord.svg


تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 0 - 6
src/assets/svg/icon-follow.svg


+ 3 - 0
src/assets/svg/icon-home-bill.svg

@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M4.73563 3H19.2644C19.4595 3 19.6466 3.0775 19.7845 3.21546C19.9225 3.35342 20 3.54053 20 3.73563V20.4713C20 20.6664 19.9225 20.8535 19.7845 20.9914C19.6466 21.1294 19.4595 21.2069 19.2644 21.2069H4.73563C4.54053 21.2069 4.35342 21.1294 4.21546 20.9914C4.0775 20.8535 4 20.6664 4 20.4713V3.73563C4 3.54053 4.0775 3.35342 4.21546 3.21546C4.35342 3.0775 4.54053 3 4.73563 3ZM5.28736 4.28736V19.9195H18.7126V4.28736H5.28736ZM6.75891 6.67917H17.2417C17.2904 6.67917 17.3372 6.69854 17.3717 6.73303C17.4062 6.76752 17.4256 6.8143 17.4256 6.86307V7.78261C17.4256 7.83139 17.4062 7.87817 17.3717 7.91266C17.3372 7.94715 17.2904 7.96652 17.2417 7.96652H6.75891C6.71013 7.96652 6.66335 7.94715 6.62887 7.91266C6.59438 7.87817 6.575 7.83139 6.575 7.78261V6.86307C6.575 6.8143 6.59438 6.76752 6.62887 6.73303C6.66335 6.69854 6.71013 6.67917 6.75891 6.67917ZM6.75891 10.1734H17.2417C17.2904 10.1734 17.3372 10.1928 17.3717 10.2273C17.4062 10.2618 17.4256 10.3086 17.4256 10.3573V11.2769C17.4256 11.3256 17.4062 11.3724 17.3717 11.4069C17.3372 11.4414 17.2904 11.4608 17.2417 11.4608H6.75891C6.71013 11.4608 6.66335 11.4414 6.62887 11.4069C6.59438 11.3724 6.575 11.3256 6.575 11.2769V10.3573C6.575 10.3086 6.59438 10.2618 6.62887 10.2273C6.66335 10.1928 6.71013 10.1734 6.75891 10.1734ZM6.75891 13.8516H13.1957C13.2445 13.8516 13.2912 13.871 13.3257 13.9054C13.3602 13.9399 13.3796 13.9867 13.3796 14.0355V14.955C13.3796 15.0038 13.3602 15.0506 13.3257 15.0851C13.2912 15.1196 13.2445 15.1389 13.1957 15.1389H6.75891C6.71013 15.1389 6.66335 15.1196 6.62887 15.0851C6.59438 15.0506 6.575 15.0038 6.575 14.955V14.0355C6.575 13.9867 6.59438 13.9399 6.62887 13.9054C6.66335 13.871 6.71013 13.8516 6.75891 13.8516Z" fill="#8EA3B9"/>
+</svg>

+ 3 - 0
src/assets/svg/icon-home-wallet.svg

@@ -0,0 +1,3 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M4.72555 3H19.2767C20.777 3 22 4.22303 22 5.72554V8.21632H15.0106C13.289 8.21632 11.8804 9.62493 11.8804 11.3466V12.147C11.8804 13.8686 13.289 15.2772 15.0106 15.2772H22V17.768C22 19.2728 20.777 20.4936 19.2745 20.4936H4.72555C3.22079 20.4936 2 19.2705 2 17.768V5.72554C2 4.22079 3.22303 3 4.72555 3ZM12.9961 11.3463C12.9961 10.8119 13.2063 10.3088 13.5886 9.92649C13.9687 9.54416 14.474 9.33398 15.0084 9.33398H21.9978V14.159H15.0084C14.474 14.159 13.9709 13.9489 13.5886 13.5665C13.2063 13.1842 12.9961 12.6811 12.9961 12.1467V11.3463ZM16.7256 11.7465C16.7256 11.2524 16.3253 10.8521 15.8312 10.8521C15.3371 10.8521 14.9368 11.2524 14.9368 11.7465C14.9368 12.2406 15.3371 12.6409 15.8312 12.6409C16.3253 12.6409 16.7256 12.2406 16.7256 11.7465Z" fill="#1D9BF0"/>
+</svg>

+ 2 - 2
src/assets/svg/icon-like.svg

@@ -1,3 +1,3 @@
-<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M15.1868 4.45988L15.1868 4.45992L15.1944 4.4639C17.5169 5.68044 17.8395 8.68879 16.3253 10.3235L15.6735 10.9752L12.4987 14.15L10.3822 16.2665C10.2295 16.4193 10.1141 16.4437 10.0527 16.4437C9.9913 16.4437 9.8759 16.4193 9.72318 16.2665L7.60666 14.15L4.43189 10.9752L3.77048 10.3138C2.14695 8.69031 2.44662 5.85632 4.77231 4.63321C6.04636 4.00006 7.81466 4.30937 8.92708 5.4218L9.45621 5.95092L9.98654 6.48126L10.5169 5.95092L11.046 5.4218C12.2324 4.23543 13.8646 3.74791 15.1868 4.45988Z" fill="#389AFF" fill-opacity="0.01" stroke="black" stroke-width="1.5"/>
+<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M16.5241 5.52953L16.5142 5.52433C15.5782 5.03216 14.225 5.28953 13.0824 6.4054L11.0951 8.34608L9.10786 6.4054C8.15668 5.47651 6.64352 5.24789 5.63883 5.72852C3.6663 6.74997 3.50591 8.96319 4.77916 10.2066L11.1706 16.4483L17.534 10.2339C18.721 8.96254 18.499 6.53976 16.534 5.53461L16.5241 5.52953ZM18.9454 11.6232L12.1519 18.2575C11.548 18.8472 10.7932 18.8472 10.1893 18.2575L3.39579 11.6232C1.13128 9.41173 1.58418 5.57855 4.75449 3.95682C6.56609 3.07224 8.98157 3.51453 10.4912 4.98883L11.0951 5.57855L11.699 4.98883C13.2086 3.51453 15.4732 2.73985 17.4357 3.77185C20.606 5.39358 21.0589 9.41173 18.9454 11.6232Z" fill="#1D9BF0"/>
 </svg>

+ 3 - 0
src/assets/svg/icon-loading-gray.svg

@@ -0,0 +1,3 @@
+<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M13.3302 6.49995C14.7109 8.89141 13.8915 11.9494 11.5 13.3301C9.10859 14.7108 6.05063 13.8914 4.66992 11.5C3.28921 9.10849 4.10859 6.05054 6.50005 4.66982C7.59822 4.03579 8.83693 3.8657 9.98924 4.09872" stroke="#B5B5B5" stroke-width="1.8"/>
+</svg>

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 8 - 0
src/assets/svg/icon-rabbit.svg


+ 2 - 2
src/assets/svg/icon-retweet.svg

@@ -1,3 +1,3 @@
-<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M5.14903 4.09573C4.95472 3.90142 4.63969 3.90142 4.44538 4.09573L2.1285 6.41261C1.8905 6.65061 1.8905 7.03649 2.1285 7.27449C2.3665 7.51249 2.75238 7.51249 2.99038 7.27449L4.18776 6.07711V12.6617C4.18776 14.2342 5.4625 15.5089 7.03497 15.5089H10.6154C10.952 15.5089 11.2248 15.2361 11.2248 14.8995C11.2248 14.5629 10.952 14.2901 10.6154 14.2901H7.03497C6.13567 14.2901 5.40664 13.561 5.40664 12.6617V6.07711L6.60403 7.27449C6.84203 7.51249 7.2279 7.51249 7.4659 7.27449C7.70391 7.03649 7.70391 6.65061 7.4659 6.41261L5.14903 4.09573ZM14.851 15.5214C15.0453 15.7157 15.3603 15.7157 15.5546 15.5214L17.8715 13.2045C18.1095 12.9665 18.1095 12.5806 17.8715 12.3426C17.6335 12.1046 17.2476 12.1046 17.0096 12.3426L15.8122 13.54V6.9554C15.8122 5.38294 14.5375 4.1082 12.965 4.1082H9.38462C9.04803 4.1082 8.77517 4.38106 8.77517 4.71764C8.77517 5.05422 9.04803 5.32708 9.38462 5.32708H12.965C13.8643 5.32708 14.5934 6.05611 14.5934 6.9554V13.54L13.396 12.3426C13.158 12.1046 12.7721 12.1046 12.5341 12.3426C12.2961 12.5806 12.2961 12.9665 12.5341 13.2045L14.851 15.5214Z" fill="black" stroke="black" stroke-width="0.1" stroke-linecap="round"/>
+<svg width="22" height="22" viewBox="0 0 22 22" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M1.50044 8.21628C1.88617 8.60144 2.51136 8.60144 2.89709 8.21628L3.84224 7.27253V14.3496C3.84224 15.169 4.11521 15.9245 4.57523 16.5307C5.23614 17.4015 6.28313 17.9639 7.46136 17.9639L11.6714 17.9639C12.2165 17.9639 12.6593 17.5225 12.6593 16.977C12.6593 16.9429 12.6575 16.9092 12.6542 16.876C12.6035 16.3781 12.1825 15.9902 11.6714 15.9902H7.46136C7.12082 15.9902 6.80456 15.8868 6.54228 15.7099C6.10513 15.4149 5.81789 14.9156 5.81789 14.3496V7.27253L6.76304 8.21628C7.14877 8.60144 7.77396 8.60144 8.15969 8.21628C8.20794 8.1681 8.25016 8.11618 8.28634 8.06145C8.53966 7.67832 8.49744 7.15757 8.15969 6.82032L5.43536 4.1C5.10101 3.76614 4.55912 3.76615 4.22477 4.1L1.50044 6.82032C1.11444 7.20575 1.11444 7.83085 1.50044 8.21628ZM20.3952 13.7829C20.0095 13.3978 19.3843 13.3978 18.9986 13.7829L18.0534 14.7267V7.64963C18.0534 7.40006 18.0281 7.15641 17.9799 6.9211C17.6423 5.27391 16.1828 4.03536 14.4343 4.03536H10.2242C10.1561 4.03536 10.0896 4.04225 10.0253 4.05539C9.57536 4.14733 9.2364 4.54491 9.2364 5.02221C9.2364 5.56769 9.67913 6.00907 10.2242 6.00907H14.4343C15.3424 6.00907 16.0778 6.74403 16.0778 7.64963V14.7267L15.1326 13.7829C14.7469 13.3978 14.1217 13.3978 13.736 13.7829C13.35 14.1684 13.35 14.7935 13.736 15.1789L16.4603 17.8992C16.7947 18.2331 17.3365 18.2331 17.6709 17.8992L20.3952 15.1789C20.4435 15.1307 20.4857 15.0788 20.5219 15.0241C20.7752 14.6409 20.733 14.1202 20.3952 13.7829Z" fill="#1D9BF0"/>
 </svg>

+ 3 - 0
src/assets/svg/icon-telegram.svg

@@ -0,0 +1,3 @@
+<svg width="42" height="42" viewBox="0 0 42 42" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M32.2165 13.8207L28.5516 30.7927C28.2752 31.9905 27.5541 32.2887 26.5295 31.7244L20.9454 27.6837L18.251 30.2284C17.9528 30.5212 17.7034 30.7661 17.1288 30.7661L17.5299 25.1815L27.8794 15.9981C28.3294 15.6042 27.7818 15.3859 27.18 15.7799L14.3855 23.6909L8.87738 21.998C7.67925 21.6306 7.65757 20.8214 9.12677 20.2571L30.6714 12.1065C31.669 11.7392 32.5418 12.3248 32.2165 13.8207V13.8207Z" fill="#259EDA"/>
+</svg>

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 16 - 0
src/assets/svg/icon-winner-more.svg


+ 4 - 0
src/assets/svg/icon-withdraw-help.svg

@@ -0,0 +1,4 @@
+<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
+<circle cx="12" cy="12" r="12" fill="#A2BCCE"/>
+<path d="M12.4852 5.5C11.1784 5.5 10.1541 5.87086 9.41239 6.63024C8.67067 7.37196 8.2998 8.37859 8.2998 9.66777H10.1541C10.1541 8.89073 10.3131 8.29029 10.6309 7.86645C10.9841 7.3543 11.5669 7.10706 12.3616 7.10706C12.9974 7.10706 13.5095 7.28366 13.8627 7.63686C14.1983 7.99007 14.3749 8.46689 14.3749 9.08499C14.3749 9.54415 14.1983 9.96799 13.8804 10.3742L13.5802 10.7097C12.4852 11.681 11.8142 12.4051 11.5846 12.8996C11.3373 13.3587 11.2314 13.9238 11.2314 14.5773V14.8775H13.121V14.5773C13.121 14.1534 13.2093 13.7649 13.4036 13.4117C13.5625 13.0938 13.8097 12.7759 14.1453 12.4934C14.9753 11.7693 15.4698 11.3102 15.6287 11.1159C16.0702 10.5508 16.2998 9.82671 16.2998 8.96137C16.2998 7.90177 15.9466 7.05408 15.2402 6.43598C14.5338 5.80022 13.6155 5.5 12.4852 5.5ZM12.1674 16.096C11.8142 16.096 11.4963 16.202 11.249 16.4492C11.0018 16.6788 10.8782 16.979 10.8782 17.3499C10.8782 17.7031 11.0018 18.0033 11.249 18.2506C11.4963 18.4978 11.8142 18.6214 12.1674 18.6214C12.5206 18.6214 12.8384 18.4978 13.0857 18.2682C13.3329 18.021 13.4742 17.7208 13.4742 17.3499C13.4742 16.979 13.3506 16.6788 13.1033 16.4492C12.8561 16.202 12.5382 16.096 12.1674 16.096Z" fill="white"/>
+</svg>

+ 58 - 129
src/entry/background.js

@@ -1,151 +1,80 @@
-console.log("hello world background todo something~");
-import { pageUrl } from '@/http/configAPI'
+// 此文件不要写具体逻辑,只调用函数
 import {
-    backTwitterPinLoginToken,
-    backTwitterPinLoginCode,
-    backHttpTwitterShortUrl
-} from "../logic/twitter.js";
+    twitterShortUrl,
+    twitterPinLoginToken,
+    twitterPinLoginCode,
+    onInstalledMid,
+    onInstalledUserSet,
+    onInstalledCreateTab,
+    checkPined,
+    popupRePublish,
+    setBadgeInfo,
+    hideBadge,
+    setMessageCount,
+    discordLoginCode,
+    saveDiscordAuthWindowId,
+    onDisconnectHandler
+} from "@/logic/background/twitter";
 
-import { setChromeStorage, getChromeCookie, LANDING_PAGE, setChromeCookie, removeChromeCookie, LANDING_PAGE_MIND } from "@/uilts/chromeExtension";
-import { guid } from '@/uilts/help.js'
+//加载bg.js 执行
+setMessageCount();
+
+// 消息通讯
+chrome.runtime.onConnect.addListener(function (port) {
+    port.onDisconnect.addListener(function() {
+        onDisconnectHandler(port);
+    });
+});
 
 chrome.runtime.onInstalled.addListener(onInstalledMethod);
 
 chrome.runtime.onMessage.addListener(onMessageMethod);
 
 function onInstalledMethod() {
-    let cookiesParams = {
-        name: 'pickup_info',
-        url: pageUrl
-    }
-    getChromeCookie(cookiesParams, (res) => {
-        let { postNickName, srcContentId } = res;
-        if (res && postNickName && srcContentId) {
-            let url = `https://twitter.com/${postNickName}/status/${srcContentId}`
-            chrome.tabs.create({
-                url
-            });
-            removeChromeCookie(cookiesParams)
-        } else {
-            chrome.tabs.create({
-                url: "https://twitter.com",
-            });
-        }
-    })
-    getChromeCookie(LANDING_PAGE_MIND, (res_arr) => {
-        // 没有cookie
-        if (res_arr && res_arr.length) {
-            setChromeStorage({ mid: JSON.stringify(res_arr[0]) })
-        } else {
-            let _params = {
-                mid: guid()
-            }
-            setChromeCookie(LANDING_PAGE, { 'mid': _params.mid })
-            setChromeStorage({ mid: JSON.stringify(_params) })
-        }
-    })
-
-    chrome.action.getUserSettings().then(res => {
-        setChromeStorage({ userSettings: JSON.stringify({ res }) })
-        // 无刷新插入js
-        chrome.tabs.query({}, (tab) => {
-            for (let i in tab) {
-                if (tab[i].url.indexOf('twitter.com') >= 0) {
-                    chrome.scripting.executeScript({
-                        target: { tabId: tab[i].id },
-                        files: ['js/content.js'],
-                    }, () => {
-                        setTimeout(() => {
-                            setChromeStorage({ executeScript: JSON.stringify({ executeScript: 1 }) })
-                        }, 2000);
-                    })
-                }
-            }
-        })
-    })
+    onInstalledCreateTab()
+    onInstalledMid()
+    onInstalledUserSet()
 }
 
 function onMessageMethod(req, sender, sendResponse) {
+    sendResponse('')
     if (req) {
-        switch (req.method) {
+        switch (req.actionType) {
             case "POPUP_LOGIN":
-                popupLogin(sendResponse);
+                twitterPinLoginToken();
                 break;
             case "POPUP_PUBLISH_TWITTER_RED_PACK":
-                setChromeStorage({
-                    popupShowPublishDialog: JSON.stringify({
-                        show: true,
-                        srcContent: req.data.srcContent,
-                        postId: req.data.postId
-                    }),
-                });
-                chrome.tabs.create({
-                    url: "https://twitter.com",
-                });
+                popupRePublish(req);
                 break;
-        }
-    }
-    return true; // remove this line to make the call sync!
-}
-
-//
-function popupLogin() {
-    backTwitterPinLoginToken();
-}
-
-// 消息通讯
-chrome.runtime.onConnect.addListener(function (port) {
-    port.onMessage.addListener(function (res) {
-        switch (res.state) {
-            case "CONTENT_SEND_CODE":
-                port.postMessage({
-                    state: "BACK_TWITTER_LOGIN_SUCCESS",
-                });
-                backTwitterPinLoginCode(res.code);
+            case 'CONTENT_GET_PINED':
+                checkPined();
                 break;
-            case "CONTENT_TWITTER_LOGIN":
-                backTwitterPinLoginToken();
+            case 'CONTENT_SET_BADGE':
+                setBadgeInfo(req);
                 break;
-            case "CONTENT_TWITTER_SHORT_LINK":
-                backHttpTwitterShortUrl(res.url).then((item) => {
-                    // port.postMessage({
-                    //     state: "BACK_TWITTER_SHORT_LINK",
-                    //     post_id: item.post_id,
-                    //     tweet_id: res.tweet_id
-                    // });
-                })
+            case 'CONTENT_HIDE_BADGE':
+                hideBadge();
                 break
-        }
-    });
-});
-
-chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
-    sendResponse('');
-    switch (req.actionType) {
-        case 'CONTENT_GET_PINED':
-            chrome.action.getUserSettings(res => {
-                let { isOnToolbar } = res;
-                console.log('isOnToolbar', isOnToolbar)
-                if (!isOnToolbar) {
-                    sendActivetabMessage({
-                        actionType: 'BG_SHOW_PIN_TIPS'
-                    });
+            case 'CONTENT_SEND_CODE':
+                twitterPinLoginCode(sender, req.code);
+            case 'CONTENT_TWITTER_LOGIN':
+                if(req.data){
+                    twitterPinLoginToken()
                 }
-            })
-            break;
+                break
+            case 'CONTENT_TWITTER_SHORT_LINK':
+                req.arr_url.forEach(item => {
+                    if (item) {
+                        twitterShortUrl(sender, item)
+                    }
+                });
+                break
+            case "CONTENT_SEND_DISCORD_AUTH_CODE":
+                discordLoginCode(req, sender);
+                break
+            case 'RED_PACKET_SAVE_DISCORD_AUTH_WINDOW_ID':
+                saveDiscordAuthWindowId(req);
+                break;
+        }
     }
-})
-
-
-function sendActivetabMessage(message = {}) {
-    chrome.tabs.query({
-        active: true,
-        currentWindow: true
-    }, (tabs) => {
-        chrome.tabs.sendMessage(tabs[0].id, message, res => {
-            console.log(res)
-        })
-    })
 }
-
-

+ 22 - 64
src/entry/content.js

@@ -1,80 +1,28 @@
-var port = chrome.runtime.connect({
-    name: "hello",
-});
+// 此文件不要写具体逻辑,只调用函数
+// var port = chrome.runtime.connect({
+//     name: "hello",
+// });
 
 import {
-    contentTwitterPinLogin,
-    renderDom,
     showGiveDialogHandler,
     showIframeHandler,
     hideIframeHandler,
     showTwitterPublishDialogHandler,
-    setIframeRedPacket,
     showPinTips,
-    addPinedPop,
-} from "../logic/twitter.js";
+    init,
+    initExecuteScript,
+    changeQueueNum
+} from "@/logic/content/twitter.js";
 
-import { getChromeStorage } from "@/uilts/chromeExtension";
-
-port.onMessage.addListener(function (res) {
-    switch (res.state) {
-        case "BACK_TWITTER_LOGIN_SUCCESS":
-            showGiveDialogHandler();
-            break;
-        // case "BACK_TWITTER_SHORT_LINK":
-        //     console.log('BACK_TWITTER_SHORT_LINK', res)
-        //     console.log('needBind', bindTwitterArt.needBind)
-        //     if (bindTwitterArt.needBind) {
-        //         bindTwitterArtMethod({ postId: res.post_id, twitterId: res.tweet_id });
-        //     }
-        //     break
-    }
-});
 
 chrome.storage.onChanged.addListener(changes => {
-    if (changes.executeScript) {
-        let item = JSON.parse(changes.executeScript.newValue)
-        if (item.executeScript) {
-            init()
-        }
-    }
+    initExecuteScript(changes)
 })
 
 window.onload = () => {
     init()
 };
 
-function init() {
-    if (window.location.href.indexOf('twitter.com') < 0) {
-        return
-    }
-    contentTwitterPinLogin(port);
-    renderDom(port);
-    setIframeRedPacket(port);
-
-    getChromeStorage("popupShowPublishDialog", (res) => {
-        console.log("popupShowPublishDialog", res);
-        if (res && res.show) {
-            setTimeout(() => {
-                showTwitterPublishDialogHandler({
-                    srcContent: res.srcContent,
-                    postId: res.postId
-                });
-            }, 1500);
-            chrome.storage.local.remove("popupShowPublishDialog");
-        }
-    });
-    getChromeStorage("userSettings", (res) => {
-        addPinedPop();
-        if (res && !res.isOnToolbar) {
-            setTimeout(() => {
-                showPinTips();
-                chrome.storage.local.remove("userSettings");
-            }, 800);
-        }
-    });
-}
-
 window.onmessage = (res) => {
     if (res.data && res.data.actionType) {
         switch (res.data.actionType) {
@@ -91,11 +39,21 @@ window.onmessage = (res) => {
     }
 };
 
+
 chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
-    sendResponse('');
+    sendResponse('')
     switch (req.actionType) {
         case 'BG_SHOW_PIN_TIPS':
             showPinTips()
-            break;
+            break
+        case "BACK_TWITTER_LOGIN_SUCCESS":
+            showGiveDialogHandler();
+            break
+        case 'BACK_TWITTER_SHORT_URL':
+            console.log('BACK_TWITTER_SHORT_URL')
+            changeQueueNum(1)
+            break
     }
-})
+})
+
+

+ 2 - 1
src/entry/popup.js

@@ -7,7 +7,7 @@ const app = createApp(App)
 import router from '@/router/popup.js'
 import "ant-design-vue/dist/antd.css"; // or 'ant-design-vue/dist/antd.less'
 
-import {Button,message} from "ant-design-vue";
+import {Button,message,Tooltip} from "ant-design-vue";
 
 
 message.config({
@@ -17,6 +17,7 @@ message.config({
 });
 app.use(Button);
 app.use(message)
+app.use(Tooltip)
 
 
 app.use(router)

+ 2 - 1
src/http/configAPI.js

@@ -1,4 +1,4 @@
-export const appVersionCode = 2
+export const appVersionCode = 3
 
 const api = {
 	production: 'https://api.denetme.net',
@@ -24,3 +24,4 @@ export const logAPIUrl = logApi[process.env.NODE_ENV] + '/log-center'
 
 export const pageUrl = page[process.env.NODE_ENV] 
 
+export const discordAuthRedirectUri = `${pageUrl}/auth/discordCallback`;

+ 37 - 0
src/http/discordApi.js

@@ -0,0 +1,37 @@
+import { service } from "./request";
+
+export function discordAuthUrl(params) {
+    return service({
+        url: `/openapi/discord/authorize`,
+        method: "post",
+        data: params,
+    });
+}
+
+/**
+ * 请求 discord Api 获取邀请服务器信息
+ * @param {} params 
+ * @returns 
+ */
+export function getInviteGuildInfo(params) {
+    return service({
+        url: `https://discord.com/api/invites/${params.inviteCode}`,
+        method: "get",
+    });
+}
+
+export function checkGuildJoined(params) {
+    return service({
+        url: `/openapi/discord/checkGuildJoined`,
+        method: "post",
+        data: params,
+    });
+}
+
+export function saveInviteGuildInfo(params) {
+    return service({
+        url: `/openapi/discord/saveInviteGuildInfo`,
+        method: "post",
+        data: params,
+    });
+}

+ 18 - 0
src/http/messageApi.js

@@ -0,0 +1,18 @@
+import { service } from "./request";
+
+export function readAllMsgByType(params) {
+    return service({
+        url: `/message/readAllMsgByType`,
+        method: 'post',
+        data: params
+    })
+}
+
+export function getAllMessageInfo(params) {
+    return service({
+        url: `/message/getStat`,
+        method: 'post',
+        data: params
+    })
+}
+

+ 19 - 10
src/http/publishApi.js

@@ -11,11 +11,11 @@ export function postPublish(params) {
 }
 
 export function verifyPaypalResult(params) {
-    return service({
-      url: `/wallet/pay/verifyPaypalResult`,
-      method: 'post',
-      data: params
-    })
+  return service({
+    url: `/wallet/pay/verifyPaypalResult`,
+    method: 'post',
+    data: params
+  })
 }
 
 export function payTaskLuckdropWithBalance(params) {
@@ -26,12 +26,21 @@ export function payTaskLuckdropWithBalance(params) {
   })
 }
 
+// 上报帖子来源平台发布事件
+export function reportSrcPublishEvent(params) {
+  return service({
+    url: `/post/reportSrcPublishEvent`,
+    method: 'post',
+    data: params
+  })
+}
+
 export function srcPublishSuccess(params) {
-    return service({
-      url: `/post/srcPublishSuccess`,
-      method: 'post',
-      data: params
-    })
+  return service({
+    url: `/post/srcPublishSuccess`,
+    method: 'post',
+    data: params
+  })
 }
 
 export function searchTwitterUser(params) {

+ 9 - 1
src/http/redPacket.js

@@ -73,4 +73,12 @@ export function getReceivedList(params){
     method: 'post',
     data: params
   }) 
-}
+}
+
+export function terminatedLuckdrop(params){
+  return service({
+    url: `/post/luckdrop/terminatedLuckdrop`,
+    method: 'post',
+    data: params
+  }) 
+}

+ 0 - 1
src/http/request.js

@@ -50,7 +50,6 @@ function checkParams(config) {
     let data = {
       ...config.data
     }
-    console.log('data', data)
     let {pageSource} = data.params || {};
     if(pageSource) {
       delete data.params.pageSource;

+ 1 - 1
src/iframe/home.js

@@ -1,5 +1,5 @@
 import { createApp } from 'vue'
-import App from '@/view/home.vue'
+import App from '@/view/iframe/home/home.vue'
 
 const app = createApp(App)
 

+ 1 - 1
src/iframe/publish.js

@@ -1,5 +1,5 @@
 import { createApp } from 'vue'
-import App from '@/view/publish.vue'
+import App from '@/view/iframe/publish/publish.vue'
 import ElementPlus from 'element-plus'
 import 'element-plus/dist/index.css'
 

+ 1 - 1
src/iframe/red-packet.js

@@ -1,5 +1,5 @@
 import { createApp } from 'vue'
-import App from '@/view/red-packet.vue'
+import App from '@/view/iframe/red-packet/red-packet.vue'
 const app = createApp(App)
 
 // 引入路由对象实例

+ 2 - 0
src/log-center/logEnum.js

@@ -59,6 +59,8 @@ export const pageSource = {
     robot_detection_failed_page: 'robot-detection-failed-page',
     // 成功领取到钱包
     received_success_page: 'received-success-page',
+    received_empty_rewards_page: 'received-empty-rewards-page',
+    
 }
 
 export const extParams = {

+ 188 - 0
src/logic/background/fetch/twitter.js

@@ -0,0 +1,188 @@
+import { appVersionCode, baseAPIUrl } from '@/http/configAPI.js'
+import { getChromeStorage } from '@/uilts/chromeExtension.js'
+
+export async function fetchTtwitterRequestToken() {
+    let storage_mid = await getChromeStorage('mid') || ''
+    const { mid } = storage_mid || {}
+    return new Promise(function (resolve, reject) {
+        let _url = `${baseAPIUrl}/user/twitterRequestToken`
+        fetch(_url, {
+            method: 'POST', // or 'PUT'
+            headers: {
+                'Content-Type': 'application/json',
+            },
+            body: JSON.stringify({
+                "baseInfo": {
+                    appVersionCode,
+                    mid
+                },
+                "params": {
+                    "oauthCallback": "oob"
+                }
+            }),
+        })
+            .then(response => response.json())
+            .then(data => {
+                resolve(data);
+            })
+            .catch((error) => {
+                reject(error);
+            });
+    })
+}
+
+export async function fetchTwitterLogin(oauthToken, oauthVerifier, receivedIds = []) {
+    let storage_mid = await getChromeStorage('mid') || ''
+    const { mid } = storage_mid || {}
+    return new Promise(function (resolve, reject) {
+        let _url = `${baseAPIUrl}/user/twitterLogin`
+        fetch(_url, {
+            method: 'POST', // or 'PUT'
+            headers: {
+                'Content-Type': 'application/json',
+            },
+            body: JSON.stringify({
+                "baseInfo": {
+                    appVersionCode,
+                    mid
+                },
+                "params": {
+                    "oauthToken": oauthToken,
+                    "oauthVerifier": oauthVerifier,
+                    "receivedIds": receivedIds
+                }
+            }),
+        })
+            .then(response => response.json())
+            .then(data => {
+                resolve(data);
+            })
+            .catch((error) => {
+                reject(error);
+            });
+
+    })
+}
+// 请求推特短链接
+export async function fetchTwitterShortUrl(url) {
+    return new Promise(function (resolve, reject) {
+        fetch(url) // 返回一个Promise对象 
+            .then((res) => {
+                return res.text() // res.text()是一个Promise对象
+            }).then((res) => {
+                resolve(res.toString());
+            })
+    })
+}
+
+
+export async function fetchAllMessageInfo(params = {}) {
+    let storage_mid = await getChromeStorage('mid') || ''
+    const { accessToken: token = '', uid = '' } = await getChromeStorage('userInfo') || {}
+    const { mid } = storage_mid || {};
+    return new Promise(function (resolve, reject) {
+        if(!token) {
+            resolve({});
+            return;
+        }
+        let _url = `${baseAPIUrl}/message/getStat`
+        fetch(_url, {
+            method: 'POST', 
+            headers: {
+                'Content-Type': 'application/json',
+            },
+            body: JSON.stringify({
+                "baseInfo": {
+                    appVersionCode,
+                    mid,
+                    machineCode: mid,
+                    token: token,
+                    uid,
+                    loginUid: uid,
+                    appType:1,
+                },
+                "params": params
+            }),
+        }).then(response => response.json())
+            .then(data => {
+                resolve(data);
+            })
+            .catch((error) => {
+                reject(error);
+            });
+    })
+}
+
+export async function fetchReadTaskAllMsg(params = {}) {
+    let storage_mid = await getChromeStorage('mid') || ''
+    const { accessToken: token = '', uid = '' } = await getChromeStorage('userInfo') || {}
+    const { mid } = storage_mid || {};
+    return new Promise(function (resolve, reject) {
+        if(!token) {
+            resolve({});
+            return;
+        }
+        let _url = `${baseAPIUrl}/message/readAllMsgByType`
+        fetch(_url, {
+            method: 'POST', 
+            headers: {
+                'Content-Type': 'application/json',
+            },
+            body: JSON.stringify({
+                "baseInfo": {
+                    appVersionCode,
+                    mid,
+                    machineCode: mid,
+                    token: token,
+                    uid,
+                    loginUid: uid,
+                    appType:1,
+                },
+                "params": params
+            }),
+        }).then(response => response.json())
+            .then(data => {
+                resolve(data);
+            })
+            .catch((error) => {
+                reject(error);
+            });
+    })
+}
+
+
+export async function getDiscordUserInfo(params = {}) {
+    let storage_mid = await getChromeStorage('mid') || ''
+    const { accessToken: token = '', uid = '' } = await getChromeStorage('userInfo') || {}
+    const { mid } = storage_mid || {};
+    return new Promise(function (resolve, reject) {
+        if(!token) {
+            resolve({});
+        }
+        let _url = `${baseAPIUrl}/openapi/discord/token`
+        fetch(_url, {
+            method: 'POST', 
+            headers: {
+                'Content-Type': 'application/json',
+            },
+            body: JSON.stringify({
+                "baseInfo": {
+                    appVersionCode,
+                    mid,
+                    machineCode: mid,
+                    token: token,
+                    uid,
+                    loginUid: uid,
+                    appType:1,
+                },
+                "params": params
+            }),
+        }).then(response => response.json())
+            .then(data => {
+                resolve(data);
+            })
+            .catch((error) => {
+                reject(error);
+            });
+    })
+}

+ 319 - 0
src/logic/background/twitter.js

@@ -0,0 +1,319 @@
+import { fetchTtwitterRequestToken, fetchTwitterLogin, fetchTwitterShortUrl, fetchAllMessageInfo, fetchReadTaskAllMsg, getDiscordUserInfo } from '@/logic/background/fetch/twitter.js'
+import { LANDING_PAGE, LANDING_PAGE_MID, setChromeStorage, setChromeCookie, getChromeCookie ,getChromeStorage, removeChromeCookie} from '@/uilts/chromeExtension.js'
+import { guid } from '@/uilts/help.js'
+import { pageUrl, discordAuthRedirectUri } from '@/http/configAPI'
+
+let authToken = ''
+export function twitterPinLoginToken() {
+    // 1.判断是否登陆了
+    getChromeStorage('userInfo', (res) => {
+        // 没有登陆
+        if (!res) {
+            fetchTtwitterRequestToken().then((res) => {
+                authToken = res.data.authToken
+                chrome.tabs.create({
+                    url: `https://api.twitter.com/oauth/authorize?oauth_token=${res.data.authToken}`
+                })
+            })
+        }
+    })
+}
+
+export function twitterPinLoginCode(sender, code) {
+    // actionType:{}
+
+    // port.postMessage({
+    //     state: "BACK_TWITTER_LOGIN_SUCCESS",
+    // });
+    // 关闭code页面
+    // chrome.tabs.query({}, (tab) => {
+    //     for (let i in tab) {
+    //         console.log(tab[i])
+    //         if (tab[i].url == 'https://api.twitter.com/oauth/authorize') {
+    //             chrome.tabs.remove(tab[i].id)
+    //         }
+    //     }
+    // })
+    chrome.tabs.sendMessage(sender.tab.id, { actionType: 'BACK_TWITTER_LOGIN_SUCCESS' }, (res) => { console.log(res) });
+    chrome.tabs.remove(sender.tab.id)
+
+
+    chrome.cookies.getAll(LANDING_PAGE, (e = []) => {
+        let _str = '[]'
+        if (e.length > 0) {
+            _str = e[0].value
+        }
+        let _arr = JSON.parse(decodeURIComponent(_str))
+        let receivedIds = []
+        if (_arr.length > 0) {
+            for (let i in _arr) {
+                receivedIds.push(_arr[i].receivedId)
+            }
+        }
+        // 发送请求
+        // token,code
+        fetchTwitterLogin(authToken, code, receivedIds).then(res => {
+            if (res.code == 0) {
+                setChromeStorage({ userInfo: JSON.stringify(res.data) })
+                // 获取全局消息数据
+                setMessageCount()
+                chrome.cookies.remove(LANDING_PAGE)
+            }
+        })
+    })
+}
+
+let discordAuthWindowId = '';
+export function saveDiscordAuthWindowId (params) {
+    let {windowId = ''} = params.data || {};
+    discordAuthWindowId = windowId;
+}
+
+export function discordLoginCode({code}, sender) {
+    console.log('sender', sender)
+    let {windowId, id} = sender.tab || {};
+    chrome.tabs.remove(id);
+
+    // 发送请求
+    getDiscordUserInfo({
+        authCode: code,
+        redirectUrl: discordAuthRedirectUri
+    }).then(res => {
+        if (res.code == 0) {
+            setTimeout(() => {
+                sendActivetabMessage({
+                    actionType: 'BACK_DISCORD_LOGIN_SUCCESS'    
+                });
+            })
+        }
+    })
+
+    // if(windowId) {
+    //     chrome.windows.remove(
+    //         windowId,
+    //         function () {
+                
+    //         }
+    //     )
+    // }
+}
+
+export function twitterShortUrl(sender, url) {
+    fetchTwitterShortUrl(url).then(res => {
+        let str_arr = res.match(/denetme.net\/([\s\S]*?)"/) || []
+        let post_Id = str_arr[1] || ''
+        if (!post_Id) {
+            return
+        }
+        // 解析
+        let _obj = {
+            short_url: url,
+            post_Id
+        }
+        getChromeStorage('cardData', item => {
+            if (item) {
+                let has = false
+                for (let i in item) {
+                    if (item[i].short_url == _obj.short_url) {
+                        item[i].short_url = _obj.short_url
+                        item[i].post_Id = _obj.post_Id
+                        setChromeStorage({ cardData: JSON.stringify(item) })
+                        has = true
+                        break
+                    }
+                }
+                if (!has) {
+                    item.push(_obj)
+                    setChromeStorage({ cardData: JSON.stringify(item) })
+                }
+
+            } else {
+                setChromeStorage({ cardData: JSON.stringify([_obj]) })
+            }
+
+            chrome.tabs.sendMessage(sender.tab.id, { actionType: 'BACK_TWITTER_SHORT_URL' }, (response) => { });
+            // port.postMessage({
+            //     state: "BACK_TWITTER_SHORT_URL"
+            // });
+        })
+    })
+}
+
+
+// 安装插件后获取mid
+export function onInstalledMid() {
+    getChromeCookie(LANDING_PAGE_MID, (res_arr) => {
+        // 没有cookie
+        if (res_arr && res_arr.length) {
+            setChromeStorage({ mid: JSON.stringify(res_arr[0]) })
+        } else {
+            let _params = {
+                mid: guid()
+            }
+            setChromeCookie(LANDING_PAGE, { 'mid': _params.mid })
+            setChromeStorage({ mid: JSON.stringify(_params) })
+        }
+    })
+}
+
+export function onInstalledUserSet() {
+    chrome.action.getUserSettings().then(res => {
+        setChromeStorage({ userSettings: JSON.stringify({ res }) })
+        // 无刷新插入js
+        chrome.tabs.query({}, (tab) => {
+            for (let i in tab) {
+                if (tab[i].url.indexOf('twitter.com') >= 0) {
+                    chrome.scripting.executeScript({
+                        target: { tabId: tab[i].id },
+                        files: ['js/content.js'],
+                    }, () => {
+                        setTimeout(() => {
+                            setChromeStorage({ executeScript: JSON.stringify({ executeScript: 1 }) })
+                        }, 2000);
+                    })
+                }
+            }
+        })
+    })
+}
+
+/**
+ * 检查是否pined 显示tips
+ */
+export function checkPined() {
+    chrome.action.getUserSettings(res => {
+        let { isOnToolbar } = res;
+        if (!isOnToolbar) {
+            sendActivetabMessage({
+                actionType: 'BG_SHOW_PIN_TIPS'
+            });
+        }
+    })
+}
+
+function sendActivetabMessage(message = {}) {
+    chrome.tabs.query({
+        active: true,
+        currentWindow: true
+    }, (tabs) => {
+        chrome.tabs.sendMessage(tabs[0].id, message, res => {
+            console.log(res)
+        })
+    })
+}
+
+
+/**
+ * 安装后打开新标签页
+ */
+export function onInstalledCreateTab() {
+    let cookiesParams = {
+        name: 'pickup_info',
+        url: pageUrl
+    }
+    getChromeCookie(cookiesParams, (res) => {
+        let { postNickName, srcContentId } = res;
+        if (res && postNickName && srcContentId) {
+            let url = `https://twitter.com/${postNickName}/status/${srcContentId}`
+            chrome.tabs.create({
+                url
+            });
+            removeChromeCookie(cookiesParams)
+        } else {
+            chrome.tabs.create({
+                url: "https://twitter.com",
+            });
+        }
+    })
+}
+
+/**
+ * 在popop重新发送
+ * @param {*} req 
+ */
+export function popupRePublish(req) {
+    setChromeStorage({
+        popupShowPublishDialog: JSON.stringify({
+            show: true,
+            srcContent: req.data.srcContent,
+            postId: req.data.postId
+        }),
+    });
+    chrome.tabs.create({
+        url: "https://twitter.com",
+    });
+}
+
+export function setBadgeInfo(params) {
+    let {text = '', color = '#DF3535'} = params.data || {};
+    chrome.action.setBadgeText({text: text});
+    chrome.action.setBadgeBackgroundColor({color: color });
+}
+
+export function hideBadge() {
+    chrome.action.setBadgeText({text: ''});
+    chrome.action.setBadgeBackgroundColor({color: [0, 0, 0, 0]});
+}
+
+export async function setMessageCount () {
+    const { accessToken: token = '', uid = '' } = await getChromeStorage('userInfo') || {}
+    if(token) {
+        getMessageInfo();
+        createAlarm();
+    }
+}
+
+function createAlarm() {
+    let alarmInfo = {
+        //1分鐘之後開始(該值不能小於1) 
+        delayInMinutes: 1, 
+        //與上方等同的寫法是 
+        // when : Date.now() + n,
+        //開始後每一分鐘執行一次(該值不能小于1) 
+        periodInMinutes : 1 
+    };
+
+    //每次加載就清空定時器
+    chrome.alarms.clearAll();
+    
+    chrome.alarms.onAlarm.addListener(function(alarm) {
+        console.log("onAlarm-", alarm);
+        getMessageInfo();
+    });
+
+    //創造定時器
+    chrome.alarms.create('denetChromeAlarm',alarmInfo);
+}
+
+export  function getMessageInfo () {
+    fetchAllMessageInfo().then(res => {
+        if(res.code == 0) {
+            let {unReadCountTotal = 0 } = res.data;
+            if(unReadCountTotal > 0) {
+                let text = unReadCountTotal > 99 ? '99+' : unReadCountTotal+'';
+                setBadgeInfo({data: {text}});
+            } else {
+                hideBadge();
+            }
+        }
+    })
+}
+
+export function readTaskAllMsg({msgType}, cb) {
+    fetchReadTaskAllMsg({
+        msgType // 1:任务红包 2:钱包明细
+    }).then(res => {
+        if(res.code == 0) {
+            cb && cb();
+        }
+    });
+}
+
+export const onDisconnectHandler = (port) => {
+    if (port.name === "popup" || port.name === "popup_transactions") {
+        let msgType = port.name === "popup" ? 1 : 2;
+        readTaskAllMsg({msgType},() => {
+            getMessageInfo();
+        })
+    }
+}

+ 212 - 0
src/logic/content/ParseCard.js

@@ -0,0 +1,212 @@
+import { getChromeStorage, setChromeStorage } from '@/uilts/chromeExtension.js'
+// 解析卡片类
+// 1.dom匹配
+// 2.找出网页匹配 获取twitterid
+// 3.获取短链接postid 
+// 4.渲染iframe twitterid
+// 5.获取红包状态页面
+// 6.查询twitterid状态
+// 7.绑定twitterid
+
+class ParseCard {
+    constructor() {
+
+    }
+    parseAllDeNetCard() {
+        let de_net_card = []
+        let arr_article = document.querySelectorAll('article') || []
+        let _txt
+        for (let i in arr_article) {
+            _txt = arr_article[i].innerText || ''
+            if (_txt.includes('#DeNet') && !this.isHasIframeByArticle(arr_article[i])) {
+                de_net_card.push({
+                    time: new Date().getTime(),
+                    dom: arr_article[i]
+                })
+            }
+        }
+        return de_net_card
+    }
+    parseCardParmas(dom_card) {
+        let tweet_Id = ''
+        let short_url = ''
+        let a_arr = dom_card.querySelectorAll('a') || []
+        a_arr = Array.from(a_arr).reverse()
+        for (let i in a_arr) {
+            // 获取推特id
+            if (a_arr[i].href && a_arr[i].href.indexOf('/status/') > 0 && !tweet_Id) {
+                tweet_Id = a_arr[i].href.split('/status/')[1] || ''
+                tweet_Id = tweet_Id.split('/')[0]
+            }
+            if (a_arr[i].href && a_arr[i].href.includes('https://t.co') && !short_url) {
+                short_url = a_arr[i].href
+            }
+            if (tweet_Id && short_url) {
+                break
+            }
+        }
+        return { tweet_Id, short_url, dom_card }
+    }
+    hideCard() {
+        if (dom.querySelector('iframe')) {
+            return
+        }
+    }
+    parseAllDeNetCardParmas() {
+        let json_data = []
+        this.parseAllDeNetCard().forEach((item) => {
+            let _obj = this.parseCardParmas(item.dom)
+            if (_obj.tweet_Id && _obj.short_url && _obj.dom_card) {
+                _obj.time = item.time
+                json_data.push(_obj)
+            }
+        })
+        return json_data
+    }
+    // 获取短链接和渲染卡片数据
+    async getCardParmas(card_json_data) {
+        let sort_link_data = await getChromeStorage('cardData') || []
+        let has_post_Id_card_data = this.getLocalHasPostIdData(sort_link_data, card_json_data)
+        let need_net_short_url = this.filterShortUrl(sort_link_data, card_json_data)
+
+        // 校验存储大小
+        let new_item = this.checkShortUrlArraySize(sort_link_data)
+        if (sort_link_data.length != new_item.length) {
+            setChromeStorage({ cardData: JSON.stringify(new_item) })
+        }
+        return {
+            has_post_Id_card_data,
+            need_net_short_url
+        }
+    }
+    filterShortUrl(sort_link_data, card_json_data) {
+        let has = false
+        let need_net_short_url = []
+
+        card_json_data.forEach((card_item) => {
+            has = false
+            sort_link_data.forEach((local_item) => {
+                if (card_item.short_url == local_item.short_url && local_item.post_Id) {
+                    has = true
+                }
+            })
+            if (!has) {
+                need_net_short_url.push(card_item.short_url)
+            }
+        })
+        // 返回的是没有postid的
+        return need_net_short_url
+    }
+    getLocalHasPostIdData(sort_link_data = [], card_json_data = []) {
+        // 
+        let has_post_Id_card_data = []
+        card_json_data.forEach((item) => {
+            let filter_item = sort_link_data.filter((filter) => {
+                return filter.short_url == item.short_url && filter.post_Id
+            })
+            if (filter_item.length > 0) {
+                item.post_Id = filter_item[0].post_Id
+                has_post_Id_card_data.push(item)
+            }
+        })
+        return has_post_Id_card_data
+    }
+    checkShortUrlArraySize(_array) {
+        if (new Blob(_array).size >= 1024 * 1024) {
+            _array.splice(0, parseInt(_array.length / 2))
+        }
+        return _array
+    }
+
+    createIframe(post_Id, tweet_Id) {
+        let _iframe = document.createElement('iframe')
+        _iframe.id = post_Id
+        _iframe.src = chrome.runtime.getURL('/iframe/red-packet.html') + `?postId=${post_Id}&tweetId=${tweet_Id}`;
+        _iframe.style.cssText = 'border: medium none; width:375px;min-height:500px;'
+        return _iframe
+    }
+    isHasIframeByArticle(dom_card) {
+        if (!dom_card || !dom_card.parentElement) {
+            return
+        }
+        if (dom_card.querySelector('iframe')) {
+            let type = 'parnet'
+            let dom = dom_card.querySelector('div[aria-labelledby]')
+            if (dom) {
+                type = 'card'
+            } else {
+                type = 'txt'
+                dom = dom_card.querySelector('div[lang][dir=auto]').parentElement
+            }
+
+            if (type == 'card') {
+                let _iframe = dom.querySelectorAll('iframe') || []
+                if (_iframe.length == 0) {
+                    dom.style.display = 'none'
+                }
+
+                if (_iframe.length == 1) {
+                    for (let i = 0; i < dom.childNodes.length; i++) {
+                        if (dom.children[i].tagName.toLowerCase() != 'iframe') {
+                            dom.children[i].style.display = 'none'
+                        }
+
+                    }
+                }
+                if (_iframe.length > 1) {
+                    for (let i = 0; i < _iframe.length; i++) {
+                        if (_iframe > 0) {
+                            _iframe[i].remove()
+                        }
+                    }
+                }
+            }else{
+                let _iframe = dom.parentElement.querySelectorAll('iframe') || []
+                if (_iframe.length > 1) {
+                    for (let i = 0; i < _iframe.length; i++) {
+                        if (_iframe > 0) {
+                            _iframe[i].remove()
+                        }
+                    }
+                }
+            }
+            return true
+        }
+        return false
+    }
+    replaceDOMRedPacket({ dom_card, tweet_Id, post_Id, time, short_url }) {
+        if (!dom_card || !dom_card.parentElement) {
+            return
+        }
+        let type
+        let dom = dom_card.querySelector('div[aria-labelledby]')
+        if (dom) {
+            type = 'card'
+            for (let i = 0; i < dom.childNodes.length; i++) {
+                if (dom.children[i].tagName.toLowerCase() != 'iframe') {
+                    dom.children[i].style.display = 'none'
+                }
+            }
+        } else {
+            type = 'txt'
+            dom = dom_card.querySelector('div[lang][dir=auto]').parentElement
+        }
+
+        dom.style = 'min-height:500px'
+        if (dom) {
+            // let div = document.createElement('div')
+            // div.style.color = 'red'
+            // div.innerText = `
+            // tweet_Id:${tweet_Id} , 
+            // post_Id:${post_Id}
+            // 获取dom时间:${time}
+            // 短链接:${short_url}
+            // 渲染时长:${(new Date().getTime() - time) / 1000}s
+            // `
+            // dom.parentElement.appendChild(div)
+            dom.appendChild(this.createIframe(post_Id, tweet_Id))
+        }
+
+    }
+}
+export default new ParseCard()

+ 187 - 364
src/logic/twitter.js → src/logic/content/twitter.js

@@ -1,117 +1,42 @@
-import { getChromeStorage, setChromeStorage, LANDING_PAGE } from '@/uilts/chromeExtension.js'
+import { getChromeStorage, setChromeStorage } from '@/uilts/chromeExtension.js'
 import { throttle } from '@/uilts/help'
-import { getTtwitterRequestToken, twitterLogin, httpTwitterShortUrl } from '../server/twitter.js'
-import { srcPublishSuccess } from '@/http/publishApi'
+import { discordAuthRedirectUri } from '@/http/configAPI'
+import { reportSrcPublishEvent } from '@/http/publishApi'
 import Report from "@/log-center/log"
 
+
 let dom = {};
 
-export function contentTwitterPinLogin(port) {
+function twitterPinLogin() {
     if (window.location.href == 'https://api.twitter.com/oauth/authorize') {
         let code = document.querySelector('code')
 
         if (code) {
-            port.postMessage({ state: 'CONTENT_SEND_CODE', code: code.innerText })
+            chrome.runtime.sendMessage({ actionType: "CONTENT_SEND_CODE", code: code.innerText }, () => { })
+            // port.postMessage({ state: 'CONTENT_SEND_CODE', code: code.innerText })
         }
     }
 }
 
-let authToken = ''
-export function backTwitterPinLoginToken() {
-    // 1.判断是否登陆了
-    getChromeStorage('userInfo', (res) => {
-        // 没有登陆
-        if (!res) {
-            getTtwitterRequestToken().then((res) => {
-                authToken = res.data.authToken
-                chrome.tabs.create({
-                    url: `https://api.twitter.com/oauth/authorize?oauth_token=${res.data.authToken}`
-                })
-            })
-        }
-    })
-}
-
-export function backTwitterPinLoginCode(code) {
-    // 关闭code页面
-    chrome.tabs.query({}, (tab) => {
-        for (let i in tab) {
-            console.log(tab[i])
-            if (tab[i].url == 'https://api.twitter.com/oauth/authorize') {
-                chrome.tabs.remove(tab[i].id)
-            }
-        }
-    })
-    chrome.cookies.getAll(LANDING_PAGE, (e = []) => {
-        let _str = '[]'
-        if (e.length > 0) {
-            _str = e[0].value
-        }
-        let _arr = JSON.parse(decodeURIComponent(_str))
-        let receivedIds = []
-        if (_arr.length > 0) {
-            for (let i in _arr) {
-                receivedIds.push(_arr[i].receivedId)
-            }
+function getDiscordAuthCode() {
+    if (window.location.href.indexOf(discordAuthRedirectUri) > -1) {
+        const urlParams = new URLSearchParams(window.location.search);
+        const code = urlParams.get('code');
+        if (code) {
+            chrome.runtime.sendMessage({ actionType: "CONTENT_SEND_DISCORD_AUTH_CODE", code }, () => { })
         }
-        // 发送请求
-        // token,code
-        twitterLogin(authToken, code, receivedIds).then(res => {
-            if (res.code == 0) {
-                setChromeStorage({ userInfo: JSON.stringify(res.data) })
-                chrome.cookies.remove(LANDING_PAGE)
-            }
-        })
     }
-    )
+};
 
-}
 
-export function backHttpTwitterShortUrl(url) {
-    return new Promise(function (resolve, reject) {
-        httpTwitterShortUrl(url).then(res => {
-            let _str_arr = res.match(/denetme.net\/([\s\S]*?)"/) || []
-            let _post_id = _str_arr[1] || ''
-            console.log('_str_arr_post_id', _post_id)
-            if (!_post_id) {
-                return
-            }
-            // 解析
-            let _obj = {
-                url,
-                post_id: _post_id
-                // tweet_id
-            }
-            getChromeStorage('sortLink', item => {
-                if (item) {
-                    for (let i in item) {
-                        if (item[i].url == _obj.url) {
-                            item[i] = _obj
-                        }
-                        // else{
-                        //     delete item[i].tweet_id
-                        // }
-                    }
-                    setChromeStorage({ sortLink: JSON.stringify(item) })
-                } else {
-                    setChromeStorage({ sortLink: JSON.stringify([_obj]) })
-                }
-                resolve({
-                    post_id: _post_id
-                })
-            })
-        })
-    })
-
-}
 
 /**
  * 渲染要插入的dom,初始化逻辑
  * @param port
  */
-export function renderDom(port) {
+function renderDom() {
     if (window.location.href.indexOf('https://twitter.com') > -1) {
-        _createBtnDom(port);
+        _createBtnDom();
         onWindowResize();
         checkHasDeBtn();
         setTimeout(() => {
@@ -150,7 +75,7 @@ export function hideIframeHandler() {
 export function showTwitterPublishDialogHandler(publishRes) {
     dom.tweetBtn.click();
     _setPublishContent(publishRes.srcContent);
-    _publishTweetEvent(publishRes.postId);
+    _publishTweetEvent(publishRes.postId, bindTwitterArtMethod);
 }
 
 export function showPinTips() {
@@ -162,7 +87,7 @@ export function showPinTips() {
     })
 }
 
-export function addPinedPop() {
+function addPinedPop() {
     let domPop = document.getElementById('de-pin-pop');
 
     if (domPop) {
@@ -262,7 +187,10 @@ function _addDeNetEditBtn(parent, dom, isClick = false) {
                         businessType: Report.businessType.buttonView,
                         objectType: Report.objectType.buttonSecond
                     });
-                    parent.parentNode.insertBefore(dom, parent.nextElementSibling);
+                    let innerDeIcon = document.getElementById('de-btn1');
+                    if (!innerDeIcon) {
+                        parent.parentNode.insertBefore(dom, parent.nextElementSibling);
+                    }
                 }
             }, 1000)
         }
@@ -336,7 +264,7 @@ function _addIframe() {
     let iframe = document.createElement('iframe');
     iframe.src = chrome.runtime.getURL('/iframe/publish.html')
     iframe.id = 'iframe-content'
-    iframe.style.cssText = 'position:fixed;top:0px;right:0;display:block; width:100%;height:100%;z-index:0; border: medium none;display:none';
+    iframe.style.cssText = 'position:fixed;top:0px;right:0;display:block; width:100%;height:100%;z-index:10000; border: medium none;display:none';
     // shadowRoot.appendChild(iframe);
     // document.body.appendChild(span)
     dom.iframe = iframe;
@@ -346,44 +274,11 @@ function _addIframe() {
     }
 }
 
-/**
- * 获取发布推文id
- * @returns {string}
- * @private
- */
-
-/** function _getTwitterArtId(contentStr, cb) {
-    let id = '';
-
-    let timer = setInterval(() => {
-        let arr = document.querySelectorAll('a') || [];
-        for (let i = 0; i < arr.length; i++) {
-            let item = arr[i];
-            if (item.innerText == '#DeNet') {
-                if (item.parentNode && item.parentNode.parentNode && item.parentNode.parentNode.innerText.length > 5) {
-                    let _postId = item.parentNode.parentNode.innerText || ''
-                    let _dom = item.parentNode.parentNode.parentNode.parentNode.parentNode
-                    let regex = new RegExp(contentStr);
-                    if (regex.test(_postId)) {
-                        id = _dom.children[0].querySelector('a[dir="auto"]').getAttribute('href').split('/status/')[1];
-                        clearInterval(timer);
-                        if (id) {
-                            cb && cb(id);
-                        }
-                        break;
-                    }
-                }
-            }
-        }
-    }, 1000);
-}
-*/
-
 /**
  * 点击deNet按钮处理
  * @private
  */
-function _deNetBtnClick(port) {
+function _deNetBtnClick() {
     getUserInfo((res) => {
         if (res) {
             if (window.location.pathname != '/home') {
@@ -405,8 +300,9 @@ function _deNetBtnClick(port) {
             setTimeout(() => {
                 dom.loadingImg.style.transform = 'rotate(0deg)'
                 dom.deBtn.innerHTML = '<span>DeNet<span>';
-            }, 3000)
-            port.postMessage({ state: 'CONTENT_TWITTER_LOGIN' })
+            }, 2000)
+
+            chrome.runtime.sendMessage({ actionType: "CONTENT_TWITTER_LOGIN", data: '1' }, (res) => { console.log(res) })
         }
     })
 }
@@ -417,7 +313,7 @@ function _deNetBtnClick(port) {
  * @private
  */
 let isSetContent = false;
-function _setPublishContent(content) {
+const _setPublishContent = throttle(function (content) {
     if (!isSetContent) {
         isSetContent = true;
         setTimeout(() => {
@@ -427,14 +323,14 @@ function _setPublishContent(content) {
             }, 2000)
         }, 1000);
     }
-}
+}, 800);
 
 /**
  * 创建deNet按钮 添加到页面
  * @returns {{deBtn2: HTMLDivElement, deBtn1: HTMLDivElement, deBtn: HTMLSpanElement}}
  * @private
  */
-function _createBtnDom(port) {
+function _createBtnDom() {
     let loadingImg = document.createElement('img');
     loadingImg.id = 'de-btn-loading'
     loadingImg.src = require("@/assets/img/icon-btn-loading.png");
@@ -471,12 +367,21 @@ function _createBtnDom(port) {
     deBtn3.style.cssText = 'width:52px;height: 52px;margin-top:20px;cursor: pointer;';
 
     deBtn.addEventListener('click', () => {
+        // chrome.runtime.sendMessage({
+        //     actionType: 'CONTENT_SET_BADGE',
+        //     data: {
+        //         text: '2'
+        //     }
+        // }, res => {
+        //     console.log(res);
+        // })
+
         Report.reportLog({
             pageSource: Report.pageSource.mainPage,
             businessType: Report.businessType.buttonClick,
             objectType: Report.objectType.buttonMain
         });
-        _deNetBtnClick(port);
+        _deNetBtnClick();
     })
     deBtn1.addEventListener('click', () => {
         Report.reportLog({
@@ -484,10 +389,10 @@ function _createBtnDom(port) {
             businessType: Report.businessType.buttonClick,
             objectType: Report.objectType.buttonSecond
         });
-        _deNetBtnClick(port);
+        _deNetBtnClick();
     })
     deBtn2.addEventListener('click', () => {
-        _deNetBtnClick(port);
+        _deNetBtnClick();
     })
     deBtn3.addEventListener('click', () => {
         Report.reportLog({
@@ -495,7 +400,7 @@ function _createBtnDom(port) {
             businessType: Report.businessType.buttonClick,
             objectType: Report.objectType.buttonMain
         });
-        _deNetBtnClick(port);
+        _deNetBtnClick();
     })
     dom.deBtn = deBtn;
     dom.deBtn1 = deBtn1;
@@ -553,257 +458,175 @@ function onWindowResize() {
 }
 
 function checkHasDeBtn() {
-    setInterval(() => {
-        try {
-            let toolBar = document.querySelector('div[data-testid="toolBar"]');
-            let innerDeIcon = document.getElementById('de-btn1');
-            if (toolBar && !innerDeIcon) {
-                let dialogScheduleBtn = _getScheduleDom(false);
-                _addDeNetEditBtn(dialogScheduleBtn, dom.deBtn1);
-            }
-        } catch (e) {
-            console.log(e)
+    try {
+        let toolBar = document.querySelector('div[data-testid="toolBar"]');
+        let innerDeIcon = document.getElementById('de-btn1');
+        if (toolBar && !innerDeIcon) {
+            let dialogScheduleBtn = _getScheduleDom(false);
+            _addDeNetEditBtn(dialogScheduleBtn, dom.deBtn1);
         }
-    }, 1000)
+    } catch (e) {
+        console.log(e)
+    }
 }
 
 /**
- * 根据postID绑定推文id
+ * 点击发推,后端绑定推特id
  */
-function bindTwitterArtMethod({ postId, twitterId }) {
-    let regex = new RegExp(bindTwitterArt.postId);
-    if (regex.test(postId)) {
-        if (twitterId && bindTwitterArt.needBind && !bindTwitterArt.isBindIng) {
-            bindTwitterArt.isBindIng = true;
-            srcPublishSuccess({
-                params: {
-                    postId: postId,
-                    srcContentId: twitterId
-                }
-            }).then((res) => {
-                if (res.code == 0) {
-                    Report.reportLog({
-                        objectType: Report.objectType.tweetPostBinded
-                    });
-                    bindTwitterArt.needBind = false;
-                    bindTwitterArt.postId = '';
-                    bindTwitterArt.isBindIng = false;
-                }
-            })
-        }
+function bindTwitterArtMethod() {
+    if (!bindTwitterArt.postId) {
+        return
     }
-}
-
-function parseDOMRedPacket() {
-    let _dom = null
-    let arr = document.querySelectorAll('a') || []
-    let _type = ''
-    for (let i in arr) {
-        if (arr[i].innerText == '#DeNet') {
-            let _tweetId = ''
-            let _article = arr[i].closest('article')
-            let _txt_area = _article.querySelector('[lang][dir=auto]')
-            let _postId = _txt_area.innerText
-            _postId = _postId.match(/###([\s\S]*?)###/)[1]
-            _dom = _article.querySelector('div[aria-labelledby]')
-            if (_dom) {
-                _type = 'card'
-            } else {
-                _type = 'parnet'
-                _dom = _txt_area
+    if (bindTwitterArt.needBind && !bindTwitterArt.isBindIng) {
+        bindTwitterArt.isBindIng = true;
+        reportSrcPublishEvent({
+            params: {
+                postId: bindTwitterArt.postId,
             }
-            let _a_area = _article.querySelector('a[aria-label]')
-            if (_a_area && _a_area.getAttribute('href')) {
-                _tweetId = _a_area.getAttribute('href').split('/status/')[1] || ''
-                if (bindTwitterArt.needBind) {
-                    bindTwitterArtMethod({ postId: _postId, twitterId: _tweetId });
-                }
+        }).then((res) => {
+            if (res.code == 0) {
+                Report.reportLog({
+                    objectType: Report.objectType.tweetPostBinded
+                });
+                bindTwitterArt.needBind = false;
+                bindTwitterArt.postId = '';
+                bindTwitterArt.isBindIng = false;
             }
-            replaceDOMRedPacket(_type, _dom, _postId, _tweetId)
-        }
+        })
     }
 }
 
-let parse_dom = {}
-
-async function parseDOMRedPacketByShortUrl(port) {
-    // let _new_time = new Date().getTime()
-    // if ((_new_time - change_time) > 1000) {
-    //     change_time = _new_time
-    // } else {
-    //     return
-    // }
-    // 为了减少声明变量次数
-    parse_dom.dom = null
-    parse_dom.txt_area = null
-    parse_dom.short_url = ''
-    parse_dom.postId = ''
-    parse_dom.a_arr = null
-    parse_dom.type = ''
-    parse_dom.tweetId = ''
-    parse_dom.article = null
-    parse_dom.a_tweetId = null
-    parse_dom.a_arr = document.querySelectorAll('a') || []
-    for (let i in parse_dom.a_arr) {
-        if (parse_dom.a_arr[i].innerText == '#DeNet') {
-            parse_dom.article = parse_dom.a_arr[i].closest('article')
-            parse_dom.dom = parse_dom.article.querySelector('div[aria-labelledby]')
-            if (parse_dom.dom && !parse_dom.dom.querySelector('iframe') && parse_dom.dom.closest('article').querySelector('iframe')) {
-                parse_dom.dom.style.display = 'none'
-                continue
-            }
-            if (parse_dom.dom && parse_dom.dom.parentElement.querySelector('iframe')) {
-                continue
-            }
-            parse_dom.short_url = getTwitterShortUrl(parse_dom.article)
-            parse_dom.a_tweetId = parse_dom.article.querySelector('a[aria-label]')
-            if (parse_dom.a_tweetId && parse_dom.a_tweetId.getAttribute('href')) {
-                parse_dom.tweetId = parse_dom.a_tweetId.getAttribute('href').split('/status/')[1] || ''
-            }
-            parse_dom.postId = await handleShortUrl(port, parse_dom.short_url)
-            console.log('_postId', parse_dom.postId)
-            console.log('short_url', parse_dom.short_url)
-
-            // 获取到postId了
-            if (parse_dom.postId) {
-                console.log('bindTwitterArt.postId', bindTwitterArt.postId)
-                console.log('parse_dom.postId', parse_dom.postId)
-                if (bindTwitterArt.needBind) {
-                    bindTwitterArtMethod({ postId: parse_dom.postId, twitterId: parse_dom.tweetId });
-                }
-                if (parse_dom.dom) {
-                    parse_dom.type = 'card'
-                } else {
-                    parse_dom.type = 'parnet'
-                    parse_dom.txt_area = parse_dom.article.querySelector('[lang][dir=auto]')
-                    parse_dom.dom = parse_dom.txt_area
-                }
-                replaceDOMRedPacket(parse_dom.type, parse_dom.dom, parse_dom.postId, parse_dom.tweetId)
-            }
-        }
-    }
-}
 
-// 校验推特短数组大小
-function checkShortUrlArraySize(_array) {
-    if (new Blob(_array).size >= 1024 * 1024) {
-        _array.splice(0, parseInt(_array.length / 2))
-    }
-    return _array
-}
+import parseCard from './ParseCard'
 
-// 获取推特短链接
-function getTwitterShortUrl(_article) {
-    let dom_arr = _article.querySelectorAll('a[href][role]')
-    let url = ''
-    for (let i in dom_arr) {
-        if (dom_arr[i].href.includes('https://t.co')) {
-            url = dom_arr[i].href
-            break
-        }
+// 检测dom改变 
+// 获取短链接 
+// 查看本地是否有postid 
+// 如果有 修改dom 返回 
+// 如果没有 网络请求 
+// 获取postid 
+// 获取twitterid 
+// 检测当前所有dom 如果没有 
+
+
+
+let queue_num = 1
+
+export const changeQueueNum = (num = 0) => {
+    queue_num = queue_num + num
+    if (queue_num > 5) {
+        queue_num = 5
     }
-    return url
 }
-// 处理短链接
-async function handleShortUrl(port, url) {
-    let post_id = ''
-    // 校验本地是否存在
-    let sort_link_data = await getChromeStorage('sortLink') || ''
-    if (sort_link_data) {
-        let _item = sort_link_data.filter((_item) => { return _item.url == url })
-        // 本地有值
-        if (_item.length > 0) {
-            if (_item[0].post_id) {
-                post_id = _item[0].post_id
-            } else {
-                // 防止多次请求,校验timeout时间
-                let _new_time = new Date().getTime()
-                if (_new_time - _item[0].time > 5000) {
-                    for (let i in sort_link_data) {
-                        if (sort_link_data[i].url == url) {
-                            sort_link_data[i].time = _new_time
-                        }
-                    }
-                    setChromeStorage({ sortLink: JSON.stringify(sort_link_data) })
-                    port.postMessage({ state: 'CONTENT_TWITTER_SHORT_LINK', url })
-                }
-            }
-        } else {
-            // 本地没有值
-            sort_link_data.push({ url, post_id: '', time: new Date().getTime() })
-            setChromeStorage({ sortLink: JSON.stringify(sort_link_data) })
-            port.postMessage({ state: 'CONTENT_TWITTER_SHORT_LINK', url })
-        }
-        // 校验存储大小
-        let new_item = checkShortUrlArraySize(sort_link_data)
-        if (sort_link_data.length != new_item.length) {
-            setChromeStorage({ sortLink: JSON.stringify(new_item) })
+
+let main_observer = null
+function onChangePageMain(targetNode) {
+    try {
+        const config = { attributes: false, childList: true, subtree: true };
+        const callback = (mutationsList, observer) => {
+            changeQueueNum(1)
         }
-    } else {
-        setChromeStorage({ sortLink: JSON.stringify([{ url, post_id: '', time: new Date().getTime() }]) })
-        port.postMessage({ state: 'CONTENT_TWITTER_SHORT_LINK', url })
+        main_observer = new MutationObserver(callback);
+        main_observer.observe(targetNode, config);
+    } catch (error) {
+        main_observer = null
     }
-    return post_id
 }
 
-function createIframe(postId, tweetId) {
-    let _iframe = document.createElement('iframe')
-    _iframe.id = postId
-    _iframe.src = chrome.runtime.getURL('/iframe/red-packet.html') + `?postId=${postId}&tweetId=${tweetId}`;
-    _iframe.style.cssText = 'border: medium none; width:375px;min-height:500px;'
-    return _iframe
-}
-function replaceDOMRedPacket(_type, _dom, postId, tweetId) {
-    if (!_dom || !_dom.parentElement) {
-        return
-    }
-    if (_dom.parentElement.querySelector('iframe')) {
-        return
-    }
+// 1.监听main改变
+// 2.监听卡片是否可见
+// 3.如果可见了 去找
 
-    if (_type == 'card') {
-        let _len
-        _len = _dom.childNodes.length
-        for (let i = 0; i < _len; i++) {
-            _dom.children[i].style.display = 'none'
+function setIframeRedPacket() {
+    // 获取所有卡片参数
+    let card_json_data = parseCard.parseAllDeNetCardParmas()
+    // 过滤出可以请求的短链接
+    parseCard.getCardParmas(card_json_data).then((res) => {
+        for (let i in res.has_post_Id_card_data) {
+            parseCard.replaceDOMRedPacket(res.has_post_Id_card_data[i])
         }
-        _dom.style = 'min-height:500px'
-        _dom.appendChild(createIframe(postId, tweetId))
-    } else {
-        let _parent = _dom.parentNode
-        _parent.appendChild(createIframe(postId, tweetId))
-    }
+        if (res.need_net_short_url.length > 0) {
+            // 请求短链接
+            chrome.runtime.sendMessage({ actionType: "CONTENT_TWITTER_SHORT_LINK", data: "", arr_url: res.need_net_short_url }, () => { })
+        }
+    })
 }
 
-// let change_time = new Date().getTime()
-// function onChangePageMain(port, targetNode) {
-//     const config = { attributes: true, childList: true, subtree: true };
-//     const callback = (mutationsList, observer) => {
-//         setTimeout(() => {
-//             parseDOMRedPacketByShortUrl(port)
-//         }, 2000)
+// 监听点击发推 按钮绑定事件
+// document.addEventListener('click', (e) => {
+//     try {
+//         if (e.target.dataset && e.target.dataset.testid && e.target.dataset.testid == 'tweetButton') {
+//             bindTwitterArtMethod()
+//         } else if (e.target.closest('div[data-testid=tweetButton]')) {
+//             bindTwitterArtMethod()
+//         }
+//     } catch (error) {
+//         console.error('error', error)
 //     }
-//     const observer = new MutationObserver(callback);
-//     observer.observe(targetNode, config);
-// }
+// })
 
-export function setIframeRedPacket(port) {
-    // let elment = document.documentElement
 
-    if (window.location.href.includes('twitter.com)')) {
-        return
+export function initExecuteScript(changes) {
+    if (changes.executeScript) {
+        let item = JSON.parse(changes.executeScript.newValue)
+        if (item.executeScript) {
+            init()
+        }
     }
-    setInterval(() => {
-        parseDOMRedPacketByShortUrl(port)
-    }, 1000)
-
-
-    // let targetNode = null
-    // let timer = setInterval(() => {
-    //     targetNode = document.querySelector('main')
-    //     if (targetNode) {
-    //         clearInterval(timer)
-    //         onChangePageMain(port, targetNode)
-    //     }
-    // }, 1000);
 }
+// 初始化
+export function init() {
+    getDiscordAuthCode();
+    if (window.location.href.indexOf('twitter.com') < 0) {
+        return
+    }
+    twitterPinLogin();
+    // 渲染dom
+    let targetNode = null
+    let timer = setInterval(() => {
+        targetNode = document.querySelector('main')
+        if (targetNode) {
+            clearInterval(timer)
+            setInterval(() => {
+                if (window.location.href.includes('twitter.com)')) {
+                    return
+                }
+                if (!main_observer) {
+                    onChangePageMain(targetNode)
+                    changeQueueNum(1)
+                }
+                console.log('queue_num', queue_num)
+                if (queue_num <= 0) {
+                    return
+                }
+                setIframeRedPacket()
+                checkHasDeBtn()
+                changeQueueNum(-1)
+            }, 1000)
+        }
+    }, 1000);
+    renderDom();
+
+
+    getChromeStorage("popupShowPublishDialog", (res) => {
+        console.log("popupShowPublishDialog", res);
+        if (res && res.show) {
+            setTimeout(() => {
+                showTwitterPublishDialogHandler({
+                    srcContent: res.srcContent,
+                    postId: res.postId
+                });
+            }, 1500);
+            chrome.storage.local.remove("popupShowPublishDialog");
+        }
+    });
+    getChromeStorage("userSettings", (res) => {
+        addPinedPop();
+        if (res && !res.isOnToolbar) {
+            setTimeout(() => {
+                showPinTips();
+                chrome.storage.local.remove("userSettings");
+            }, 800);
+        }
+    });
+}

+ 9 - 4
src/manifest.json

@@ -2,7 +2,7 @@
     "manifest_version": 3,
     "name": "DeNet",
     "description": "Growing more twitter followers with Denet",
-    "version": "1.0.2",
+    "version": "1.0.3",
     "background": {
         "service_worker": "/js/background.js"
     },
@@ -19,7 +19,10 @@
         {
             "matches": [
                 "https://*.twitter.com/*",
-                "https://twitter.com/*"
+                "https://twitter.com/*",
+                "*://testh5.denetme.net/*",
+                "*://h5.denetme.net/*",
+                "*://preh5.denetme.net/*"
             ],
             "run_at": "document_start",
             "js": [
@@ -27,7 +30,7 @@
             ]
         }
     ],
-      "host_permissions": [
+    "host_permissions": [
         "*://*.twitter.com/*",
         "*://twitter.com/*",
         "*://testh5.denetme.net/*",
@@ -35,12 +38,14 @@
         "*://preh5.denetme.net/*"
     ],
     "permissions": [
+        "declarativeNetRequest",
         "tabs",
         "action",
         "cookies",
         "activeTab",
         "scripting",
-        "storage"
+        "storage",
+        "alarms"
     ],
     "options_page": "options.html",
     "web_accessible_resources": [

+ 0 - 4
src/rules/rules_1.json

@@ -10,10 +10,6 @@
                     "header": "X-Frame-Options",
                     "operation": "remove"
                 },
-                {
-                    "header": "Frame-Options",
-                    "operation": "remove"
-                },
                 {
                     "header": "Content-Security-Policy",
                     "operation": "remove"

+ 0 - 76
src/server/twitter.js

@@ -1,76 +0,0 @@
-import { appVersionCode, baseAPIUrl } from '@/http/configAPI.js'
-import { getChromeStorage } from '@/uilts/chromeExtension.js'
-
-export async function getTtwitterRequestToken() {
-    let storage_mid = await getChromeStorage('mid') || ''
-    const { mid } = storage_mid || {}
-    return new Promise(function (resolve, reject) {
-        let _url = `${baseAPIUrl}/user/twitterRequestToken`
-        fetch(_url, {
-            method: 'POST', // or 'PUT'
-            headers: {
-                'Content-Type': 'application/json',
-            },
-            body: JSON.stringify({
-                "baseInfo": {
-                    appVersionCode,
-                    mid
-                },
-                "params": {
-                    "oauthCallback": "oob"
-                }
-            }),
-        })
-            .then(response => response.json())
-            .then(data => {
-                resolve(data);
-            })
-            .catch((error) => {
-                reject(error);
-            });
-    })
-}
-
-export async function twitterLogin(oauthToken, oauthVerifier, receivedIds = []) {
-    let storage_mid = await getChromeStorage('mid') || ''
-    const { mid } = storage_mid || {}
-    return new Promise(function (resolve, reject) {
-        let _url = `${baseAPIUrl}/user/twitterLogin`
-        fetch(_url, {
-            method: 'POST', // or 'PUT'
-            headers: {
-                'Content-Type': 'application/json',
-            },
-            body: JSON.stringify({
-                "baseInfo": {
-                    appVersionCode,
-                    mid
-                },
-                "params": {
-                    "oauthToken": oauthToken,
-                    "oauthVerifier": oauthVerifier,
-                    "receivedIds": receivedIds
-                }
-            }),
-        })
-            .then(response => response.json())
-            .then(data => {
-                resolve(data);
-            })
-            .catch((error) => {
-                reject(error);
-            });
-
-    })
-}
-// 请求推特短链接
-export async function httpTwitterShortUrl(url) {
-    return new Promise(function (resolve, reject) {
-        fetch(url) // 返回一个Promise对象 
-            .then((res) => {
-                return res.text() // res.text()是一个Promise对象
-            }).then((res) => {
-                resolve(res.toString());
-            })
-    })
-}

+ 1 - 1
src/uilts/chromeExtension.js

@@ -5,7 +5,7 @@ export const LANDING_PAGE = {
     url: pageUrl
 }
 
-export const LANDING_PAGE_MIND = {
+export const LANDING_PAGE_MID = {
     name: 'mid',
     url: pageUrl
 }

+ 1 - 1
src/view/components/message-box.vue

@@ -50,7 +50,7 @@ const confirm = () => {
     right: 0;
     bottom: 0;
     left: 0;
-    z-index: 1000;
+    z-index: 3000;
     height: 100%;
     background-color: rgba(0, 0, 0, 0.5);
     overflow: auto;

+ 54 - 1
src/view/components/options-transactions.vue → src/view/components/popup-transactions.vue

@@ -15,6 +15,8 @@
         <div class="list-wrapper" ref="listContent">
             <div>
                 <div class="cell" v-for="(item, index) in dataList" :key="index">
+                    <red-dot class="red-dots"
+                        v-if="item.unReadMsgCount > 0"></red-dot> 
                     <div class="img-wrapper">
                         <!-- 收入- 任务红包领取 -->
                         <template v-if="item.bizType == 1">
@@ -141,9 +143,13 @@
 
 <script setup>
 /* eslint-disable */
-import { onMounted, ref, defineProps, defineEmits } from "vue";
+import { onMounted, ref, defineProps, defineEmits, onBeforeUnmount } from "vue";
+
+import redDot from "@/view/components/red-dot.vue";
 
 import { transactionsList } from "@/http/account";
+import { readAllMsgByType, getAllMessageInfo } from "@/http/messageApi"
+import { setBadgeInfo, hideBadge } from "@/logic/background/twitter";
 var moment = require('moment');
 
 let dataList = ref([]);
@@ -184,9 +190,47 @@ const getTransactionsList = () => {
 };
 
 onMounted(() => {
+    chrome.runtime.connect({ name: "popup_transactions" });
+    readAllMsgByType({
+        params: {
+            msgType: 1
+        }
+    }).then(res => {
+        setMessageCount();
+    });
     getTransactionsList();
 });
 
+onBeforeUnmount(() => {
+    readAllMsgByType({
+        params: {
+            msgType: 1 // 1:任务红包 2:钱包明细
+        }
+    });
+    readAllMsgByType({
+        params: {
+            msgType: 2
+        }
+    });
+})
+
+
+const setMessageCount = () => {
+    getAllMessageInfo({params: {
+    }}).then(res => {
+        if(res.code == 0) {
+            let {unReadCountTotal = 0 } = res.data;
+            if(unReadCountTotal > 0) {
+                let text = unReadCountTotal > 99 ? '99+' : text+'';
+                setBadgeInfo({data: {text}});
+            } else {
+                hideBadge();
+            }
+        }
+    });
+}
+
+
 const emits = defineEmits(["back"]);
 
 const back = () => {
@@ -248,6 +292,13 @@ const listScroll = (e) => {
             min-height: 66px;
             box-sizing: border-box;
             padding-left: 20px;
+            position: relative;
+
+            .red-dots {
+                position: absolute; 
+                right: 4px; 
+                top: 4px;
+            }
 
             .img-wrapper {
                 position: relative;
@@ -282,6 +333,8 @@ const listScroll = (e) => {
                         font-weight: 500;
                         font-size: 14px;
                         margin-bottom: 5px;
+                        word-break: break-all;
+                        max-width: 120px;
                     }
 
                     .time {

+ 1 - 1
src/view/components/option-withdraw.vue → src/view/components/popup-withdraw.vue

@@ -63,7 +63,7 @@
                             </span>
                         </template>
                         <template v-else>
-                            计算中
+                            calculating
                         </template>
                     </div>
                     <div>{{ walletWithdrawConfig.withdrawFeeDesc }}</div>

+ 26 - 0
src/view/components/red-dot.vue

@@ -0,0 +1,26 @@
+<template>
+    <div class="red-dot" 
+        :style="{width: size + 'px', height: size + 'px',
+        background: color}">
+    </div>
+</template>
+
+<script setup>
+import { defineProps } from "vue";
+defineProps({
+    size: {
+        type: String,
+        default: '8'
+    },
+    color: {
+        type: String,
+        default: '#FF0000'
+    },
+});
+</script>
+
+<style lang="scss" scoped>
+.red-dot {
+    border-radius: 50%;
+}
+</style>

+ 1 - 1
src/view/home.vue → src/view/iframe/home/home.vue

@@ -9,7 +9,7 @@
 
 <script setup>
 import { onMounted, ref } from "vue";
-import popup from './popup/index.vue'
+import popup from '@/view/popup/index.vue'
 
 let contentStyle = ref({});
 

+ 3 - 3
src/view/components/follow-input.vue → src/view/iframe/publish/components/follow-input.vue

@@ -255,7 +255,7 @@ const onUserMouseLeave = (params, index) => {
         width: 100%;
         display: flex;
         flex-wrap: wrap;
-        padding: 0px 0 10px 18px;
+        padding: 0px 0 4px 18px;
         box-sizing: border-box;
 
         .icon-add{
@@ -278,7 +278,7 @@ const onUserMouseLeave = (params, index) => {
             position: relative;
             margin-right: 10px;
             background-color: #fff;
-            margin-top: 10px;
+            margin-top: 4px;
 
             .icon-del {
                 width: 18px;
@@ -375,7 +375,7 @@ const onUserMouseLeave = (params, index) => {
         .icon-add-wrapper {
             display: flex;
             align-items: center;
-            margin-top: 14px;
+            margin-top: 10px;
         }
     }
 </style>

+ 51 - 0
src/view/iframe/publish/components/get-more.vue

@@ -0,0 +1,51 @@
+<template>
+    <div class="getMore" @click="jumpMore">
+        <img width="20" :src=" require('@/assets/svg/icon-big-give.svg') " />
+        <font>Get More Giveaway</font>
+        <img height="20" :src=" require('@/assets/svg/icon-cell-arrow-right.svg') " />
+      </div>
+</template>
+
+<script setup>
+import { getFrontConfig } from "@/http/account";
+import { onBeforeMount, ref } from "vue";
+
+// const
+const moreUrl = ref('');
+
+onBeforeMount(() => {
+    getFrontConfig({
+        params: {},
+    }).then(res => {
+        moreUrl.value = res.data.moreLuckdropsUrl;
+    })
+})
+
+const jumpMore = () => {
+    if (moreUrl.value) {
+        window.open(moreUrl.value)
+    }
+}
+
+</script>
+
+<style lang="scss" scoped>
+.getMore {
+    display: flex;
+    height: 50px;
+    cursor: pointer;
+    user-select: none;
+    align-items: center;
+    justify-content: center;
+    box-shadow: 0px -2px 10px rgba(0, 0, 0, 0.06);
+    img {
+        margin: 0 5px;
+    }
+    font {
+        color: #000;
+        font-size: 15px;
+        font-weight: 500;
+        line-height: 18px;
+    }
+}
+</style>

+ 2 - 1
src/view/components/paypal-button.vue → src/view/iframe/publish/components/paypal-button.vue

@@ -32,7 +32,7 @@
         <div class="pay-btn">
             <div class="iframe-pay"
                 v-show="currentCurrencyInfo.currencyCode == 'USD'"
-                 @click="paypalPay">
+                @click="paypalPay">
                 <iframe
                     class="iframe-pay"
                     ref="iframe"
@@ -185,6 +185,7 @@ onMounted(() => {
             color: #1D9BF0;
             display: flex;
             align-items: center;
+            justify-content: end;
             margin-bottom: 6px;
             span {
                 display: inline-block;

+ 7 - 7
src/view/components/preview-card.vue → src/view/iframe/publish/components/preview-card.vue

@@ -92,10 +92,9 @@
 <script setup>
 import { ref, defineProps, onMounted, nextTick, watch, reactive } from "vue";
 
-import customCardCover from './custom-card-cover.vue'
+import customCardCover from '@/view/components/custom-card-cover.vue'
 
 import {getChromeStorage} from "@/uilts/chromeExtension"
-import { throttle } from "@/uilts/help"
 import {getUser} from "@/http/publishApi"
 
 let userInfo = ref({});
@@ -161,7 +160,8 @@ const getUserName = (screenName) => {
 
 const calcPreviewCanvasParams = () => {
     nextTick(() => {
-        let domHeight = document.querySelector('.card-container').offsetHeight;
+        let containerDom = document.querySelector('.card-container');
+        let domHeight = containerDom && containerDom.offsetHeight || 500;
         const canvasHeight = 820, canvasWidth = 600;
         if(domHeight < canvasHeight) {
             //比例: 高 / 宽
@@ -195,9 +195,9 @@ onMounted(() => {
             installStatus.value = !installStatus.value;
         }, 3000)
     });
-    window.addEventListener('resize', throttle(function () {
+    window.addEventListener('resize',function () {
         calcPreviewCanvasParams();
-    }, 300))
+    })
 })
 
 </script>
@@ -270,7 +270,7 @@ onMounted(() => {
             }
         }
         .left {
-            background: url('../../assets/img/img-preview-bg-after.png');
+            background: url('@/assets/img/img-preview-bg-after.png');
             width: 387px;
             height: 100%;
             background-size: contain;
@@ -308,7 +308,7 @@ onMounted(() => {
 
 
         .content-before {
-            background: url('../../assets/img/img-preview-bg-before.png');
+            background: url('@/assets/img/img-preview-bg-before.png');
             background-size: contain;
             height: 100%;
             .head {

+ 0 - 0
src/view/components/top-up.vue → src/view/iframe/publish/components/top-up.vue


+ 358 - 86
src/view/components/give-dialog.vue → src/view/iframe/publish/give-dialog.vue

@@ -7,16 +7,17 @@
             <div class="pop-mask"  
                 v-show="showCurrencyPop" 
                 @click.stop="showCurrencyPop = false"></div>
+
             <!-- 头部 -->
             <div class="head">
                 <div class="left">
                     <!-- 关闭按钮 -->
                     <div class="close-btn" @click="close">
                         <img  class="icon-close"
-                            :src="require('../../assets/svg/icon-close.svg')"
+                            :src="require('@/assets/svg/icon-close.svg')"
                             v-if="showComType == 'default'"/>
                         <img class="icon-close"
-                            :src="require('../../assets/svg/icon-back.svg')"
+                            :src="require('@/assets/svg/icon-back.svg')"
                             v-else/>
                     </div>
                     <!-- 标题 -->
@@ -41,18 +42,22 @@
                     </div>
                 </div>
             </div>
+
             <!-- 内容 -->
             <div class="body">
                 <img src="@/assets/gif/icon-guide-select.gif"
                     class="icon-guide-select"
                     v-if="!currentCurrencyInfo.currencyCode && showComType == 'default'">
+
                 <!-- 充值组件 -->
                 <top-up v-if="showComType == 'topUp'" 
                     :asyncIng="asyncIng"
                     :currentCurrencyInfo="tempCurrentCurrencyInfo"
                     @topUpDone="topUpDone"></top-up>
+
                 <!-- 表单填写容器 -->
                 <div class="body-content" v-if="showComType != 'topUp'">
+
                     <!-- 货币列表  -->
                     <div class="currency-pop" v-show="showCurrencyPop">
                         <currency-list 
@@ -64,7 +69,7 @@
                     <div class="left" v-if="showComType != 'preview'">
                         <div class="gift-pack-wrapper">
                             <img class="icon"
-                                :src="require('../../assets/svg/icon-gift-pack.svg')"/>
+                                :src="require('@/assets/svg/icon-gift-pack.svg')"/>
                         </div>
                         <div class="bottom">
                         </div>
@@ -77,6 +82,7 @@
                                 class="img-mode"
                                 :src="require('@/assets/svg/img-mode.svg')"
                             />
+
                             <!-- 金额、数量 -->
                             <div class="form-base">
                                 <div class="item currency-select-wrapper">
@@ -95,7 +101,7 @@
                                         <img
                                             class="arrow"
                                             :src="currentCurrencyInfo.currencyCode ?
-                                                require('../../assets/svg/icon-form-arrow-down.svg') :  require('../../assets/svg/icon-form-white-arrow-down.svg') 
+                                                require('@/assets/svg/icon-form-arrow-down.svg') :  require('@/assets/svg/icon-form-white-arrow-down.svg') 
                                             "
                                         />
                                     </div>
@@ -112,7 +118,7 @@
                                         <img
                                             class="icon"
                                             :src="
-                                                require('../../assets/svg/icon-winners.svg')
+                                                require('@/assets/svg/icon-winners.svg')
                                             "
                                         />
                                         Winners
@@ -125,13 +131,14 @@
                                     />
                                 </div>
                             </div>
+
                             <!-- 刷新按钮、充值 -->
                             <div class="form-base-help" v-show="currentCurrencyInfo.currencyCode">
                                 <div class="currency-operation">
                                     <div class="balance">
                                         <img
                                             :src="
-                                                require('../../assets/svg/icon-balance.svg')
+                                                require('@/assets/svg/icon-balance.svg')
                                             "
                                         />
                                         Balance
@@ -142,7 +149,7 @@
                                             :class="{ 'icon-refresh-rotate': refreshRotate }"
                                             @click="updateCurrencyBanlce"
                                             :src="
-                                                require('../../assets/svg/icon-form-refresh.svg')
+                                                require('@/assets/svg/icon-form-refresh.svg')
                                             "
                                         />
                                     </div>
@@ -153,6 +160,7 @@
                                 </div>
                             </div>
                             <div class="form-label">Tasks</div>
+
                             <!-- 转推、like、关注 -->
                             <div class="form-require">
                                 <div
@@ -160,33 +168,53 @@
                                     v-for="(item, index) in formList"
                                     :key="index"
                                 >
-                                    <div class="label">
-                                        <img
-                                            class="icon"
-                                            :src="item.icon"
-                                        />
-                                        {{ item.label }}
-                                    </div>
-                                    <div
-                                        class="control"
-                                        v-if="item.nodeType == 'textarea'"
-                                    >
-                                        <follow-input
-                                            :isAddSelf="!isBack"
-                                            :atUserList="atUserList"
-                                            @addUser="addFollowUser"
-                                            @setUser="setFollowUser"
-                                            @delUser="delFollowUser"
-                                        ></follow-input>
+                                    <div class="item-left">
+                                        <div class="label">
+                                            <img
+                                                class="icon"
+                                                :src="item.icon"
+                                            />
+                                            {{ item.label }}
+                                        </div>
+                                        <div
+                                            class="control"
+                                            v-if="item.nodeType == 'textarea'"
+                                        >
+                                            <follow-input
+                                                :isAddSelf="!isBack"
+                                                :atUserList="atUserList"
+                                                @addUser="addFollowUser"
+                                                @setUser="setFollowUser"
+                                                @delUser="delFollowUser"
+                                            ></follow-input>
+                                        </div>
+                                        <!-- join discord -->
+                                        <div
+                                            class="control"
+                                            v-if="item.nodeType == 'input'"
+                                        >
+                                            <div v-if="showDiscordInvitePop" 
+                                                class="discord-invite-info"
+                                                @click="showDiscordInvitePop = false">
+                                                <img class="icon" :src="discordInviteInfo.icon || require('@/assets/svg/icon-discord-mini.svg')" />
+                                                <span class="name">{{discordInviteInfo.name}}</span>
+                                            </div>
+                                            <input v-model="item.text" 
+                                                placeholder="Enter discord invite link"
+                                                class="discord-address" 
+                                                @input="onIptDiscordAddress($event, index)"
+                                                @blur="onBlurDiscordAddress($event, index)" />
+                                        </div>
                                     </div>
                                     <el-switch
-                                        v-if="item.type == 2"
+                                        v-if="item.type == 2 || item.type == 7"
                                         v-model="item.checked"
+                                        @change="formSwitchChange($event, item, index)"
                                     />
                                     <img
                                         v-if="item.type == 3"
                                         :src="
-                                            require('../../assets/svg/icon-option-checked.svg')
+                                            require('@/assets/svg/icon-option-checked.svg')
                                         "
                                     />
                                 </div>
@@ -196,26 +224,27 @@
                                 <div class="label">
                                     <img
                                         :src="
-                                            require('../../assets/svg/icon-anti-bot.svg')
+                                            require('@/assets/svg/icon-anti-bot.svg')
                                         "
                                         class="icon-bot"
                                     />
                                     Anti Bot
                                     <img
                                         :src="
-                                            require('../../assets/svg/icon-beta.svg')
+                                            require('@/assets/svg/icon-beta.svg')
                                         "
                                         class="icon-beta"
                                     />
                                     <img
                                         :src="
-                                            require('../../assets/svg/icon-question.svg')
+                                            require('@/assets/svg/icon-question.svg')
                                         "
                                         class="icon-question"
                                     />
                                 </div>
                                 <el-switch v-model="openAntiBot" />
                             </div> -->
+
                             <!-- 提示 -->
                             <ul class="tips-wrapper">
                                 <li class="row">
@@ -233,17 +262,15 @@
                                 <div class="submit-btn"
                                     :class="{ 'disabled-submit': iptErrMsgTxt != '' && !depositGuide }"
                                     @click="confirm">
-                                    <img
-                                        class="icon-loading"
+                                    <img  class="icon-loading"
                                         v-if="submitIng"
-                                        :src="
-                                            require('../../assets/svg/icon-btn-loading.svg')
-                                        "
+                                        :src="require('@/assets/svg/icon-btn-loading.svg')"
                                     />
                                     {{iptErrMsgTxt ? iptErrMsgTxt : 'NEXT'}}
                                 </div>
                             </div>
                         </div>
+
                         <!-- 预览 -->
                         <template v-else-if="showComType == 'preview'">
                             <preview-card
@@ -253,6 +280,7 @@
                                 :amountFontSize="previewFontSize"
                             ></preview-card>
                         </template>
+
                         <!-- paypal支付按钮 -->
                         <div v-show="showComType == 'preview'">
                             <paypal-button
@@ -272,6 +300,7 @@
                 </div>
             </div>
         </div>
+        
         <!-- 提示 -->
         <message-box
             :dialogVisible="showMessageBox"
@@ -286,22 +315,24 @@
 <script setup>
 import { ref, watch, reactive, defineProps, defineEmits, onMounted, nextTick } from "vue";
 import { postPublish, verifyPaypalResult, syncChainTokenRechargeRecord, getCurrencyInfoByCode } from "@/http/publishApi";
+import { getInviteGuildInfo, saveInviteGuildInfo } from "@/http/discordApi";
 import { payCalcFee, getPayConfig } from "@/http/pay";
 import { getFrontConfig } from "@/http/account";
 import {setChromeStorage, getChromeStorage} from "@/uilts/chromeExtension"
-import { throttle } from "@/uilts/help"
+import { debounce } from "@/uilts/help"
 import Report from "@/log-center/log"
 import { ElMessage, ElLoading } from "element-plus";
 import "element-plus/es/components/message/style/css";
 
 import {create, all} from "mathjs";
 
-import previewCard from "./preview-card";
-import paypalButton from "./paypal-button";
-import followInput from "./follow-input";
-import messageBox from "./message-box.vue";
-import currencyList from "./currency-list.vue";
-import topUp from "./top-up.vue"
+import messageBox from "@/view/components/message-box.vue";
+import currencyList from "@/view/components/currency-list.vue";
+
+import previewCard from "@/view/iframe/publish/components/preview-card";
+import followInput from "@/view/iframe/publish/components/follow-input";
+import paypalButton from "@/view/iframe/publish/components/paypal-button";
+import topUp from "@/view/iframe/publish/components/top-up.vue";
 
 const config = {
     number: 'BigNumber',
@@ -412,10 +443,15 @@ let currentCurrencyInfo = ref({
     usdEstimateBalance: ""
 });
 
+const discordIptErrTxt = 'Discord invite link is wrong';
+const discordIptEmptyErrTxt = 'Enter discord invite link';
+const discordIptNerverExpiresErrTxt = 'Make sure the Discord link never expires'
+let iptErrType = ''; //discord
+
 let formList = reactive([
     {
         label: "Follow",
-        icon: require("../../assets/svg/icon-follow.svg"),
+        icon: require("@/assets/svg/icon-follow.svg"),
         nodeType: "textarea",
         type: 1,
         text: [],
@@ -423,20 +459,31 @@ let formList = reactive([
     },
     {
         label: "Retweet",
-        icon: require("../../assets/svg/icon-retweet.svg"),
+        icon: require("@/assets/svg/icon-retweet.svg"),
         nodeType: "div",
         type: 3,
         checked: true,
     },
     {
         label: "Like Tweet",
-        icon: require("../../assets/svg/icon-like.svg"),
+        icon: require("@/assets/svg/icon-like.svg"),
         nodeType: "div",
         type: 2,
         checked: true,
     },
+    {
+        label: "Join Discord",
+        icon: require("@/assets/svg/icon-discord-mini.svg"),
+        nodeType: "input",
+        text: '',
+        type: 7,
+        checked: true,
+    },
 ]);
 
+const discordInviteInfo = ref({});
+let showDiscordInvitePop = ref(false);
+
 const props = defineProps({
     dialogVisible: {
         type: Boolean,
@@ -528,6 +575,19 @@ const getPayAmount = async (amountValue) => {
     return res.data;
 };
 
+const saveDiscordGuildInfo = () => {
+    let {guildId, inviteCode, inviteUrl} = discordInviteInfo.value;
+    if(guildId && inviteCode && inviteUrl) {
+        saveInviteGuildInfo({
+            params: {
+                guildId,
+                inviteCode,
+                inviteUrl
+            }
+        })
+    }
+}
+
 
 const confirm = () => {
     if(depositGuide.value) { //余额不够去充值
@@ -541,6 +601,7 @@ const confirm = () => {
     if (!totalCount) {
         return;
     }
+    saveDiscordGuildInfo();
     submitRequest();
 };
 
@@ -707,7 +768,13 @@ const submitRequest = async () => {
             let relatedUsers = formList[i]["text"];
             item.relatedUsers = relatedUsers;
             finishConditions.push(item);
-        } else if (formList[i]["checked"]) {
+        } else  if (formList[i]["type"] == 7) {
+            // join discord
+            if(formList[i]["checked"] && formList[i]["text"]) {
+                item.bizData = JSON.stringify({inviteUrl: formList[i]["text"]});
+                finishConditions.push(item);
+            }
+        } else  if (formList[i]["checked"]) {
             finishConditions.push(item);
         }
     }
@@ -769,8 +836,11 @@ const calcFontSize = (str, domWidth, maxSize) => {
  */
 const initParams = () => {
     resetFormIpt();
+
+    // clear follow value
     formList[0].text = [];
     atUserList.value = [];
+
     submitIng.value = false;
     isBack.value = false;
     showCurrencyPop.value = false;
@@ -778,6 +848,11 @@ const initParams = () => {
 
     tempCurrentCurrencyInfo.value = {};
     currentCurrencyInfo.value = {};
+
+    // clear discord value
+    formList[3].text = '';
+
+    discordInviteInfo.value = {};
 };
 
 /**
@@ -921,25 +996,6 @@ const calcIptValue = (cb) => {
         };
     }
 
-    // let num = amountValue, scale = 1;
-    // if(amountValue.indexOf('.') > -1) {
-    //     num = amountValue.toString();
-    //     let obj = scaleNumber(num);
-    //     num = obj.val;
-    //     scale = obj.scale;
-    // }
-    // let minAmount = currentCurrencyInfo.value.minAmount;
-    // // 输入的token数量或者法币金额,平均分到每个红包,是否小于最小单位
-    // if (num / totalCount  < minAmount * scale) {
-    //     flag = false;
-    // }
-
-    // return {
-    //     flag,
-    //     count: Math.floor((num / minAmount * scale)/ (scale * scale))
-    // };
-    // console.log('baseFormData',`${baseFormData.amountValue} / ${baseFormData.totalCount}`)
-
     if (math.format(math.evaluate(`${baseFormData.amountValue} / ${baseFormData.totalCount}`)) < +currentCurrencyInfo.value.minAmount) {
         flag = false;
     }
@@ -952,24 +1008,14 @@ const calcIptValue = (cb) => {
 /**
  * 设置输入提示语
  */
-const setInputErrorMsg = (params) => {
-
-    // let amountValue = baseFormData.amountValue;
-    // let num = amountValue, scale = 1;
-    // if(amountValue.indexOf('.') > -1) {
-    //     num = amountValue.toString();
-    //     let obj = scaleNumber(num);
-    //     num = obj.val;
-    //     scale = obj.scale;
-    // }
-
-    onIptSetErrorTxt(params);
+const setInputErrorMsg = () => {
+    onIptSetErrorTxt();
 };
 
 /**
  * 输入时 检测设置错误信息
  */
-const onIptSetErrorTxt = (params) => {
+const onIptSetErrorTxt = (params = {}) => {
     depositGuide.value = false;
     if(!currentCurrencyInfo.value.currencyCode) {
         iptErrMsgTxt.value = "Select a reward"
@@ -983,6 +1029,9 @@ const onIptSetErrorTxt = (params) => {
             iptErrMsgTxt.value = `${baseFormData.amountValue} ${currentCurrencyInfo.value.tokenSymbol} Can send up to ${res.count} winners`;
         } else {
             iptErrMsgTxt.value = "";
+            if(params.actionType != 'discord_blur') {
+                setDiscordErrTxt({getDuildId: true});
+            }
         }
     } else if(currentCurrencyInfo.value.currencyCode != 'USD') {
         depositGuide.value = true;
@@ -990,6 +1039,179 @@ const onIptSetErrorTxt = (params) => {
     } 
 }
 
+/**
+ * 监听开关触发事件
+ */
+const formSwitchChange = (val, params, index) => {
+    console.log('formSwitchChange')
+    if(!val) {
+        if(params.type == 7) {
+            //错误类型 discord 清空discord错误校验
+            if(iptErrType == 'discord') {
+                iptErrMsgTxt.value = '';
+                formList[index]['text'] = '';
+                onIptSetErrorTxt();
+            }
+        }
+    } else {
+        onIptSetErrorTxt();
+    }
+}
+
+/** 监听 discord 输入 */
+const onIptDiscordAddress = (e, index) => {
+    let val = formList[index].text;
+    let checked = formList[index].checked;
+    if(val && !checked) {
+        checked = true;
+        formList[index].checked = checked;
+        formList[index].text = formList[index].text.replace(/\s/g,'');
+    }
+    onIptDiscordDebounce()
+}
+
+const onBlurDiscordAddress = (e, index) => {
+    setDiscordErrTxt({fromType: 'discord', showPop: false, actionType: 'discord_blur'});
+}
+
+
+const getDiscordIptData = () => {
+    let discordItem = formList.find(item => item.type == 7);
+    return discordItem;
+}
+
+/**
+ * 设置输入discord错误提示信息
+ */
+const setDiscordErrTxt = (params = {showPop: false}) => {
+    let discordData = getDiscordIptData() || {};
+    if(discordData.checked) {
+        if(discordData.text) {
+            let validata = checkInviteUrl(discordData.text);
+            if(validata) {
+                getDiscordInviteInfo({inviteUrl: discordData.text, getDuildId: params.getDuildId}, (res) => {
+                    console.log('discordData',res)
+                    // 未知的邀请链接
+                    if(res.inviteCode != res.data.code || !res.data.guildId) {
+                        iptErrMsgTxt.value = discordIptErrTxt;
+                        iptErrType = 'discord';
+                    } else {
+                        if(res.data.expires !== null) {
+                            // 不是永久邀请链接
+                            iptErrMsgTxt.value = discordIptNerverExpiresErrTxt;
+                            iptErrType = '';
+                        } else {
+                            if(iptErrMsgTxt.value) {
+                                iptErrMsgTxt.value = '';
+                                iptErrType = '';
+                            }
+                            if(params.showPop && res.data) {
+                                showDiscordInvitePop.value = true;
+                                setTimeout(() => {
+                                    showDiscordInvitePop.value = false;
+                                }, 2000)
+                            }
+                            if(params.fromType == 'discord') {
+                                onIptSetErrorTxt();
+                            }
+                        }
+                    }
+                })
+            } else {
+                iptErrMsgTxt.value = discordIptErrTxt;
+                iptErrType = 'discord';
+            }
+        } else {
+            if(params.actionType == 'discord_blur') {
+                onIptSetErrorTxt({acitonType: 'discord_blur'});
+            } else {
+                // 设置空提示
+                iptErrMsgTxt.value = discordIptEmptyErrTxt;
+                iptErrType = 'discord';
+            }
+        }
+    }
+}
+
+const onIptDiscordDebounce = debounce(function() {
+    setDiscordErrTxt({fromType: 'discord', showPop: true});
+}, 800) 
+
+/**
+ * 校验 discord邀请url
+ */
+const checkInviteUrl = (inviteUrl) => {
+    let flag = false;
+    const INVITE_URL_PREFIX_1 = 'https://discord.gg/';
+    const INVITE_URL_PREFIX_2 = 'https://discord.com/invite/';
+    const INVITE_URL_PREFIX_3 = 'http://discord.gg/';
+    const INVITE_URL_PREFIX_4 = 'http://discord.com/invite/';
+    const INVITE_URL_PREFIX_5 = 'discord.gg/';
+    const INVITE_URL_PREFIX_6 = 'discord.com/invite/';
+
+    const arr = [INVITE_URL_PREFIX_1, INVITE_URL_PREFIX_2, INVITE_URL_PREFIX_3, INVITE_URL_PREFIX_4, INVITE_URL_PREFIX_5, INVITE_URL_PREFIX_6]
+
+    if(inviteUrl) {
+        if(arr.indexOf(inviteUrl) > -1) {
+            flag = false;
+        } else {
+            let isPass = false;
+            for(let i = 0; i < arr.length; i++) {
+                let item = arr[i];
+                if(inviteUrl.startsWith(item)) {
+                    isPass = true;
+                    break;
+                }
+            }
+            flag = isPass;
+        }
+    } 
+
+    return flag;
+}
+
+/**获取discord 邀请信息 */
+const getDiscordInviteInfo = ({inviteUrl, getDuildId}, cb) => {
+    if(!inviteUrl) return;
+    let inviteCode = '';
+    let arr = inviteUrl.split('/');
+    if(arr.length > 0) {
+        inviteCode = arr[arr.length - 1];
+    }
+    if(!getDuildId && discordInviteInfo.value.guildId && discordInviteInfo.value.inviteCode == inviteCode) {
+        return;
+    }
+    getInviteGuildInfo({
+        inviteCode
+    }).then(res => {
+        if(!res) {
+            res = {};
+        }
+        let {name, icon, id} = res.guild || {};
+
+        icon = icon && id ? `https://cdn.discordapp.com/icons/${id}/${icon}.png` : '';
+        let resData = {
+            inviteCode,
+            data: {
+                code: res.code,
+                guildId: id,
+                inviteUrl,
+                inviteCode,
+                expires: res.expires_at,
+                name,
+                icon,
+            }
+        }
+        discordInviteInfo.value = resData.data;
+        cb && cb(resData);
+    }).catch((err) => {
+        if(iptErrMsgTxt.value && iptErrType == 'discord') {
+            iptErrMsgTxt.value = '';
+            iptErrType = '';
+        }
+    });
+}
+
 /**
  * 获取支付配置(paypalClientId)
  */
@@ -1065,17 +1287,13 @@ onMounted(() => {
 </script>
 
 <style lang="scss" scoped>
-.ql-container {
-    height: 100px;
-}
-
 .overlay {
     position: fixed;
     top: 0;
     right: 0;
     bottom: 0;
     left: 0;
-    z-index: 10000;
+    z-index: 2000;
     height: 100%;
     background-color: rgba(0, 0, 0, 0.5);
     overflow: auto;
@@ -1089,6 +1307,7 @@ onMounted(() => {
         top: 50%;
         transform: translate(-50%, -50%);
         box-sizing: border-box;
+        z-index: 2000;
 
         .pop-mask {
             width: 100%;
@@ -1449,9 +1668,15 @@ onMounted(() => {
                             justify-content: space-between;
                             margin: 0 16px;
                             border-bottom: 1px solid #ececec;
+                            padding: 8px 0;
+                            box-sizing: border-box;
+                            
+                            .item-left {
+                                display: flex;
+                            }
 
                             .label {
-                                min-width: 88px;
+                                min-width: 76px;
                                 display: flex;
                                 align-items: center;
                                 font-size: 15px;
@@ -1463,10 +1688,57 @@ onMounted(() => {
                             }
 
                             .control {
-                                width: 100%;
-                                min-height: 50px;
+                                min-width: 280px;
                                 margin-left: 18px;
                                 box-sizing: border-box;
+                                border-left: 1px solid #ECECEC;
+                                position: relative;
+
+                                .discord-address {
+                                    border: none;
+                                    outline: none;
+                                    color: #1D9BF0;
+                                    font-weight: 500;
+                                    font-size: 14px;
+                                    width: 100%;
+                                    height: 34px;
+                                    padding-left: 15px;
+                                }
+                                .discord-address::placeholder {
+                                    color: #c5c5c5;
+                                }
+
+                                .discord-invite-info {
+                                    position: absolute;
+                                    top: 40px;
+                                    left: 0;
+                                    box-shadow: 0px 4px 30px rgba(0, 0, 0, 0.3);
+                                    background: #fff;
+                                    border-radius: 16px;
+                                    padding: 16px;
+                                    box-sizing: border-box;
+                                    display: flex;
+                                    align-items: center;
+                                    cursor: pointer;
+
+                                    .icon {
+                                        width: 40px;
+                                        height: 40px;
+                                        margin-right: 10px;
+                                        border-radius: 50%;
+                                    }
+                                    .name  {
+                                        font-weight: 600;
+                                        font-size: 16px;
+                                        color: #101419;
+                                        width: 193px;
+                                        height: 20px;
+                                        overflow: hidden;
+                                        white-space: nowrap;
+                                        text-overflow: ellipsis;
+                                        display: inline-block;
+                                    }
+                                }
                             }
                         }
                         .form-item:last-child {

+ 2 - 4
src/view/publish.vue → src/view/iframe/publish/publish.vue

@@ -1,3 +1,4 @@
+<!-- 发布弹窗 -->
 <template>
     <div class="main_app">
         <give-dialog
@@ -10,7 +11,7 @@
 
 <script setup>
 import { ref } from "vue";
-import giveDialog from "@/view/components/give-dialog.vue";
+import giveDialog from "@/view/iframe/publish/give-dialog.vue";
 
 let dialogVisible = ref(false);
 
@@ -38,9 +39,6 @@ window.addEventListener("message", function (event) {
 </script>
 
 <style>
-.tox-metions__card-common {
-    display: none !important;
-}
 .main_app {
 }
 </style>

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 405 - 243
src/view/iframe/red-packet/red-packet.vue


+ 1 - 1
src/view/options.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="main_app">
-    <h1>Hello {{msg}}</h1>
+    <!-- <h1>Hello {{msg}}</h1> -->
   </div>
 </template>
 

+ 52 - 23
src/view/popup/components/head.vue

@@ -1,9 +1,12 @@
 <template>
-    <div class="head" :class="{ 'border': show_state != 'home' }">
+    <div class="head" :class="{ 'border': show_state != 'home', 'home': show_state == 'home' }">
         <template v-if="show_state == 'home'">
+            <div class="desc">
+                <img class="img" :src="user_info.avatarUrl" />
+                <font class="name">{{user_info.nickName}}</font>
+            </div>
             <div class="logo">
                 <img :src="require('@/assets/svg/icon-denet-logo.svg')" />
-                <span>DeNet</span>
             </div>
         </template>
         <template v-else>
@@ -11,23 +14,26 @@
             <div class="title">{{ props.title }}</div>
             <img :src="require('@/assets/svg/icon-refresh.svg')" alt="" class="refresh" v-if="show_refresh"
                 @click="clickRefresh" :class="{ transform_rotate: state.rotate }">
-
-        </template>
-        <img :src="require('@/assets/svg/icon-more-l.svg')" alt="" class="more" v-if="props.show_more"
-            @click="state.show_option = true">
-        <div class="area-option" v-if="state.show_option" @click="state.show_option = false">
-            <div class="option">
-                <div class="item" @click="clickItem('/transactions')">
-                    <img :src="require('@/assets/svg/icon-menu.svg')" alt="">
-                    <span>Transactions History</span>
+            <img :src="require('@/assets/svg/icon-withdraw-help.svg')" alt="" class="help" v-if="props.show_help"
+                @click="clickHelp">
+            <img :src="require('@/assets/svg/icon-more-l.svg')" alt="" class="more" v-if="props.show_more"
+                @click="state.show_option = true">
+            <div class="area-option" v-if="state.show_option" @click="state.show_option = false">
+                <div class="option">
+                    <div class="item" @click="clickItem('/transactions')">
+                        <img :src="require('@/assets/svg/icon-menu.svg')" alt="">
+                        <span>Transactions History</span>
+                    </div>
                 </div>
             </div>
-        </div>
+        </template>
     </div>
 </template>
 <script setup>
-import { defineProps, defineEmits, reactive, ref } from "vue";
+import { getChromeStorage } from "@/uilts/chromeExtension";
+import { defineProps, defineEmits, reactive, ref, onMounted } from "vue";
 import router from "@/router/popup.js";
+import { boolean } from "mathjs";
 
 let props = defineProps({
     title: String,
@@ -35,7 +41,9 @@ let props = defineProps({
     show_refresh: Boolean,
     show_option: Boolean,
     show_more: Boolean,
-    back_url: String
+    show_help: Boolean,
+    back_url: String,
+    user_info: Object
 })
 
 let state = reactive({
@@ -69,6 +77,10 @@ function clickItem(path) {
     router.push(path)
 }
 
+function clickHelp() {
+    window.open(`https://aboard-cattle-610.notion.site/How-to-withdraw-assets-from-DeNet-to-MetaMask-01c679bb9ff441429e31e8f7c1f67411`)
+}
+
 </script>
 <style lang="scss" scoped>
 .border {
@@ -83,20 +95,33 @@ function clickItem(path) {
     align-items: center;
     padding: 0 12px;
 
+    &.home {
+        height: 64px;
+    }
+
     .logo {
         display: flex;
         align-items: center;
-
-        span {
-            font-size: 13px;
-            margin-left: 5px;
-            font-weight: 500;
-
+        img {
+            width: 26px;
+            height: 26px;
         }
+    }
 
-        img {
-            width: 16px;
-            height: 16px;
+    .desc {
+        .img {
+            width: 34px;
+            height: 34px;
+            overflow: hidden;
+            border-radius: 50%;
+            margin-right: 10px;
+        }
+        .name {
+            display: inline-block;
+            width: 200px;
+            color: #000000;
+            font-size: 16px;
+            font-weight: bold;
         }
     }
 
@@ -164,6 +189,10 @@ function clickItem(path) {
         margin-right: 20px;
     }
 
+    .help {
+        margin-right: 12px;
+    }
+
     .title {
         padding-left: 16px;
         flex: 1;

+ 0 - 0
src/view/components/option-login.vue → src/view/popup/components/login.vue


+ 122 - 0
src/view/popup/components/modal.vue

@@ -0,0 +1,122 @@
+<template>
+    <div class="msg-box-overlay" v-if="visible">
+        <div class="content-wrapper">
+            <div class="title">{{title}}</div>
+            <div class="desc">{{content}}</div>
+            <div class="btn-wrapper">
+                <div class="btn cancel" @click="cancel">
+                    {{cancelText}}
+                </div>
+                <div class="btn confirm" @click="confirm">
+                    {{confirmText}}
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+/* eslint-disable */
+import { defineEmits, defineProps } from "vue";
+const props = defineProps({
+    visible: {
+        type: Boolean,
+        default: false,
+    },
+    title: {
+        type: String,
+        default: ''
+    },
+    content: {
+        type: String,
+        default: ''
+    },
+    cancelText: {
+        type: String, 
+        default: 'Cancel'
+    },
+    confirmText: {
+        type: String, 
+        default: 'Confirm'
+    }
+});
+
+const emits = defineEmits(["cancel", "confirm"]);
+
+
+const cancel = () => {
+    emits("cancel", {});
+};
+
+const confirm = () => {
+    emits("confirm", {});
+};
+
+</script>
+
+<style lang="scss" scoped>
+.msg-box-overlay {
+    position: fixed;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    z-index: 1000;
+    height: 100%;
+    background-color: rgba(0, 0, 0, 0.8);
+    overflow: auto;
+
+    .content-wrapper {
+        position: absolute;
+        left: 50%;
+        top: 50%;
+        width: 335px;
+        min-height: 190px;
+        background: #FFFFFF;
+        border-radius: 20px;
+        padding: 20px 13px;
+        box-sizing: border-box;
+        transform: translate(-50%, -50%);
+        text-align: center;
+
+        .title {
+            font-weight: 600;
+            font-size: 18px;
+            margin-bottom: 13px;
+        }
+        .desc {
+            min-height: 44px;
+            font-weight: 400;
+            font-size: 15px;
+            margin-bottom: 20px;
+        }
+        .btn-wrapper {
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+
+            .btn {
+                width: 150px;
+                height: 46px;
+                border-radius: 100px;
+                box-sizing: border-box;
+                font-weight: 600;
+                font-size: 16px;
+                cursor: pointer;
+                display: flex;
+                align-items: center;
+                justify-content: center;
+            }
+
+            .cancel {
+                color: #fff;
+                background: #FF0000;
+            }
+            .confirm {
+                color: #000;
+                border: 1px solid #000000;
+            }
+        }
+    }
+}
+</style>

+ 272 - 94
src/view/popup/popup.vue

@@ -1,31 +1,31 @@
 <template>
     <div class="page-wrapper" ref="pageWrapperDom" @scroll="pageScroll">
         <template v-if="isLogin && homeVisibility">
-            <!-- <div class="nav-bar">
-                <div class="item left">
-                    <img :src="require('@/assets/svg/icon-denet-logo.svg')" />
-                    DeNet
-                </div>
-                <div class="item right" @click="showTransactions">
-                    <img :src="require('@/assets/svg/icon-option-list.svg')" />
-                    Transactions
-                </div>
-            </div> -->
-            <v-head :show_more="true" :show_state="'home'"></v-head>
+            <v-head :show_more="true" :show_state="'home'" :user_info="userInfo"></v-head>
             <div class="content">
-                <img :src="require('@/assets/svg/icon-money.svg')" class="icon-money" />
-                <div class="msg" v-show="walletWithdrawConfig.withdrawUSDPaypalFeeDesc">
-                    ({{ walletWithdrawConfig.withdrawUSDPaypalFeeDesc }})</div>
+                <div class="balance">
+                    <div class="wallet">
+                        <img :src="require('@/assets/svg/icon-home-wallet.svg')" />
+                        <font>Balance Valuation</font>
+                    </div>
+                    <div class="bill" @click="showTransactions">
+                        <red-dot class="red-dot" v-if="unReadCountWallet > 0"></red-dot>
+                        <img :src="require('@/assets/svg/icon-home-bill.svg')" />
+                    </div>
+                </div>
                 <div class="amount">${{ canWithdrawBalance }}</div>
                 <div class="area-btn">
                     <div class="withdraw-btn" @click="clickWithdraw">Withdraw</div>
                     <div class="top-up-btn" @click="clickTopUp">Deposit</div>
                 </div>
-
             </div>
+
             <div class="tab-bar">
-                <div class="tab-item" :class="{ active: currentTabIndex == index }" v-for="(item, index) in tabList"
-                    :key="index" @click="clickTab(item, index)">
+                <div class="tab-item" 
+                    :class="{ active: currentTabIndex == index }" 
+                    v-for="(item, index) in tabList"
+                    :key="index" 
+                    @click="clickTab(item, index)">
                     <img :src="item.icon" class="icon" />
                     {{ item.label }}
                 </div>
@@ -33,8 +33,15 @@
             <div class="list-wrapper" ref="pageGiveListDom">
                 <div class="give-list" v-if="currentTabIndex == 0">
                     <template v-if="giveList.length">
-                        <div class="cell" v-for="(item, index) in giveList" :key="index"
+                        <div class="cell" 
+                            :class="{'cell-center': item.type == 1}"
+                            v-for="(item, index) in giveList" 
+                            :key="index"
                             @click="clickListItem(item, index)">
+
+                            <red-dot class="red-dots"
+                                v-if="item.unReadMsgCount > 0"></red-dot>
+
                             <div class="img-wrapper">
                                 <!-- 收到红包 -->
                                 <template v-if="item.type == 1">
@@ -45,12 +52,15 @@
                                 </template>
                                 <!-- 发出去红包 -->
                                 <template v-else-if="2">
-                                    <img :src="
+                                    <img 
+                                        class="icon-big-give"
+                                        :src="
                                         require('@/assets/svg/icon-list-big-give.svg')
                                     " />
                                 </template>
                             </div>
-                            <div class="info-wrapper">
+                            <div class="info-wrapper"
+                                :class="{'info-center': item.type == 1}">
                                 <div class="left">
                                     <div class="nickname">
                                         {{
@@ -102,41 +112,50 @@
                                             </template>
                                             <!-- 进行中 -->
                                             <template v-else-if="item.status == 1">
-                                                {{
-                                                        item.postTaskLuckdrop
-                                                            .receivedCount
-                                                }}/{{
-        item.postTaskLuckdrop
-            .totalCount
-}}
+                                                {{item.postTaskLuckdrop.receivedCount}}/{{item.postTaskLuckdrop.totalCount}}
                                             </template>
-                                            <!-- 已结束 -->
-                                            <template v-else-if="item.status == 2">
-                                                (Time expired)
+                                            <!-- 2:已结束; 3:提前终止-->
+                                            <template v-else-if="item.status == 2 || item.status == 3">
+                                                ({{item.status == 2 ? 'Time expired' : 'Termination'}})
                                                 {{
                                                         item.postTaskLuckdrop
                                                             .receivedCount
                                                 }}/{{
-        item.postTaskLuckdrop
-            .totalCount
-}}
+                                                        item.postTaskLuckdrop
+                                                            .totalCount
+                                                }}
                                             </template>
-                                            <!-- 红包未发出显示 -->
-                                            <div v-if="item.postTaskLuckdrop.reSendAvailable" class="send-btn"
-                                                @click.stop="sendTwitter(item)">
-                                                Send
+                                            <!-- 红包提前终止/退款(进行中)显示-->
+                                            <template v-if="item.status == 4">
+                                                Terminating
+                                            </template>
+
+                                            <!-- 进行中或者未发送成功时显示 
+                                                v-if="item.status == 1 || item.postTaskLuckdrop.reSendAvailable"-->
+                                            <div  class="desc-bottom-bar">
+                                                <!-- 没有终止红包时显示 -->
+                                                <div v-if="item.postTaskLuckdrop.terminatedAvailable"
+                                                    class="btn"
+                                                    @click.stop="terminaHandler(item, index)">
+                                                    Termination
+                                                </div>
+                                                
+                                                <!-- 红包未发出显示 -->
+                                                <div class="btn send-btn"
+                                                    v-if="item.postTaskLuckdrop.reSendAvailable"       
+                                                    @click.stop="sendTwitter(item)">
+                                                    Send
+                                                </div>
+                                                <div v-else-if="item.srcContentId"
+                                                    class="btn detail-btn"
+                                                    @click.stop="clickListItem(item, index)">details</div>
                                             </div>
                                         </div>
                                     </div>
-                                    <!-- 发红包,红包未发出隐藏-->
-                                    <img v-if="
-                                        !(
-                                            item.type == 2 &&
-                                            item.postTaskLuckdrop.reSendAvailable
-                                        )
-                                    " class="icon" :src="
-    require('@/assets/svg/icon-cell-arrow-right.svg')
-" />
+                                    <!-- 发红包—— 未发出、进行中 隐藏 -->
+                                    <img v-if="item.type != 2" 
+                                        class="icon" 
+                                        :src="require('@/assets/svg/icon-cell-arrow-right.svg')" />
                                 </div>
                             </div>
                         </div>
@@ -166,26 +185,40 @@
             </div>
         </template>
         <!-- login -->
-        <option-login v-if="!isLogin" @loginAction="loginAction" />
+        <popup-login v-if="!isLogin" @loginAction="loginAction" />
         <template v-if="isLogin && !homeVisibility">
             <!-- 交易列表 -->
-            <option-transactions v-if="transactionsVisibility" @back="transactionsBack" />
+            <popup-transactions v-if="transactionsVisibility" @back="transactionsBack" />
             <!-- 提现页 -->
-            <option-withdraw :amountValue="canWithdrawBalance" :walletWithdrawConfig="walletWithdrawConfig"
+            <popup-withdraw :amountValue="canWithdrawBalance" :walletWithdrawConfig="walletWithdrawConfig"
                 v-if="withdrawVisibility" @back="withdrawBack" />
         </template>
+        <modal :visible="modalVisible"   
+            title="Early termination of Giveaway?"
+            content="This operation will close the red envelope and refund the remaining amount within 2 days"
+            cancelText="Termination"
+            confirmText="Cancel"
+            @cancel="modalCancel"
+            @confirm="modalConfirm" />
     </div>
 </template>
 
 <script setup>
-import { ref, onMounted, inject } from "vue";
-import optionTransactions from "@/view/components/options-transactions";
-import optionLogin from "@/view/components/option-login.vue";
-import optionWithdraw from "@/view/components/option-withdraw.vue";
+import { ref, onMounted, inject, onBeforeUnmount } from "vue";
+
+import popupLogin from "@/view/popup/components/login.vue";
+import popupTransactions from "@/view/components/popup-transactions";
+import popupWithdraw from "@/view/components/popup-withdraw.vue";
+import redDot from "@/view/components/red-dot.vue";
+import modal from "@/view/popup/components/modal.vue";
+
 import {
     getChromeStorage,
 } from "@/uilts/chromeExtension";
 import { getBalance, getMineLuckdropRecords } from "@/http/account";
+import { readAllMsgByType, getAllMessageInfo } from "@/http/messageApi"
+import { terminatedLuckdrop } from "@/http/redPacket";
+import { setBadgeInfo, hideBadge } from "@/logic/background/twitter";
 import Report from "@/log-center/log";
 import router from "@/router/popup.js";
 import VHead from '@/view/popup/components/head.vue'
@@ -202,6 +235,9 @@ let isLogin = ref(false);
 let homeVisibility = ref(false);
 let transactionsVisibility = ref(false);
 let withdrawVisibility = ref(false);
+let modalVisible = ref(false);
+
+let terminaTask = {};
 
 let userInfo = ref({});
 let canWithdrawBalance = ref(0);
@@ -213,6 +249,9 @@ let isRequestWithdrawBalance = ref(false);
 let currentTabIndex = ref(0);
 let giveList = ref([]);
 
+// 钱包未读数
+let unReadCountWallet = ref(0);
+
 let giveReqParams = {
     params: {
         pageNum: 1,
@@ -234,9 +273,19 @@ withdraw_info.paypal.wallet_withdraw_config = walletWithdrawConfig
 let moreTabList = ref([
     {
         icon: require("@/assets/svg/icon-twitter.svg"),
-        label: "Twitter contact",
+        label: "Twitter",
         href: 'https://twitter.com/denet2022'
     }
+    , {
+        icon: require("@/assets/svg/icon-discord.svg"),
+        label: "Discord",
+        href: 'https://discord.gg/wZSz9p8ddG'
+    }
+    // , {
+    //     icon: require("@/assets/svg/icon-telegram.svg"),
+    //     label: "Telegram",
+    //     href: 'https://t.me/denetpro'
+    // }
 ]);
 
 let tabList = ref([
@@ -255,6 +304,7 @@ onMounted(() => {
         if (isLogin.value) {
             getAccountBalance();
             getLuckdropRecordsList();
+            chrome.runtime.connect({ name: "popup" });
             Report.reportLog({
                 pageSource: Report.pageSource.denetHomePage,
                 businessType: Report.businessType.pageView,
@@ -268,8 +318,38 @@ onMounted(() => {
             });
         }
     });
+    setMessageCount();
+    setTimeout(() => {
+        setMessageCount();
+    }, 1600)
 });
 
+onBeforeUnmount(() => {
+    readAllMsgByType({
+        params: {
+            msgType: 1
+        }
+    }).then(res => {
+        setMessageCount();
+    })
+});
+
+const setMessageCount = () => {
+    getAllMessageInfo({params: {
+    }}).then(res => {
+        if(res.code == 0) {
+            let {unReadCountTotal = 0, unReadCountWalletDetail = 0, unReadCountTaskLuckdrop = 0} = res.data;
+            unReadCountWallet.value = unReadCountWalletDetail;
+            if(unReadCountTotal > 0) {
+                let text = unReadCountTotal > 99 ? '99+' : unReadCountTotal+'';
+                setBadgeInfo({data: {text}});
+            } else {
+                hideBadge();
+            }
+        }
+    });
+}
+
 const moreItemHandle = (params) => {
     window.open(params.href);
 }
@@ -363,7 +443,6 @@ const clickListItem = (params) => {
     if (!params.srcContentId) {
         return;
     }
-    let url = "";
     let twitterUrl = "https://twitter.com/";
     let nickName = "";
     if (params.type == 1) {
@@ -371,7 +450,7 @@ const clickListItem = (params) => {
     } else if (params.type == 2) {
         nickName = userInfo.value.nickName;
     }
-    url = twitterUrl + nickName + "/status/" + params.srcContentId;
+    let url = twitterUrl + nickName + "/status/" + params.srcContentId;
 
     chrome.tabs.create({
         url,
@@ -406,11 +485,11 @@ const withdrawBack = () => {
 };
 
 
-// const showTransactions = () => {
+const showTransactions = () => {
 //     // homeVisibility.value = false;
 //     // transactionsVisibility.value = true;
-//     router.push('/transactions')
-// };
+    router.push('/transactions')
+};
 
 // const clickWithdraw = () => {
 //     if (isRequestWithdrawBalance.value) {
@@ -437,9 +516,12 @@ const login = () => {
 /**
  * sendMessage
  */
-const callEventPageMethod = (method, data, callback) => {
+const callEventPageMethod = (actionType, data, callback) => {
     chrome.runtime.sendMessage(
-        { method: method, data: data },
+        { 
+            actionType: actionType, 
+            data: data 
+        },
         function (response) {
             if (typeof callback === "function") callback(response);
         }
@@ -453,7 +535,10 @@ const sendTwitter = (params) => {
     console.log(params)
     callEventPageMethod(
         "POPUP_PUBLISH_TWITTER_RED_PACK",
-        { srcContent: params.postTaskLuckdrop.srcContent, postId: params.postTaskLuckdrop.postId },
+        { 
+            srcContent: params.postTaskLuckdrop.srcContent, 
+            postId: params.postTaskLuckdrop.postId 
+        },
         function (response) {
             console.log("res", response);
         }
@@ -477,10 +562,38 @@ const clickTopUp = () => {
     router.push('/top-up/home');
 }
 
+const terminaHandler = (params, index) => {
+    terminaTask = params;
+    terminaTask.index = index;
+    modalVisible.value = true;
+}
+
+const modalCancel = () => {
+    //请求终止接口 id terminaTask.id 、 刷新当前列表、 关闭 
+    modalVisible.value = false;
+    let index = terminaTask.index;
+    terminatedLuckdrop({
+        params: {
+            luckdropId: terminaTask.id
+        }
+    }).then(res => {
+        if(res.code == 0) {
+            giveList.value[index]['status'] = res.data.status;
+            giveList.value[index]['postTaskLuckdrop']['reSendAvailable'] = false;
+            giveList.value[index]['postTaskLuckdrop']['terminatedAvailable'] = false;
+        }
+    });
+    terminaTask = {};
+}
+
+const modalConfirm = () => {
+    modalVisible.value = false;
+    terminaTask = {};
+}
 
 </script>
 
-<style lang="scss">
+<style lang="scss" scoped>
 html,
 body {
     padding: 0 !important;
@@ -523,24 +636,56 @@ body {
     }
 
     .content {
-        margin-top: 30px;
-        text-align: center;
+        padding: 20px;
+        background-color: #F0F8FE;
 
         .icon-money {
             width: 70px;
             height: 70px;
         }
 
+        .balance {
+            display: flex;
+            justify-content: space-between;
+            .wallet {
+                img {
+                    width: 24px;
+                    height: 24px;
+                    margin-right: 6px;
+                    vertical-align: middle;
+                }
+                font {
+                    color: #000;
+                    font-size: 14px;
+                    font-weight: bold;
+                }
+            }
+            .bill {
+                position: relative;
+                img {
+                    width: 24px;
+                    height: 24px;
+                    cursor: pointer;
+                }
+                .red-dot {
+                    position: absolute;
+                    right: 0px;
+                    top: -1px;
+                }
+            }
+        }
+
         .amount {
-            margin-top: 16px;
+            margin-top: 20px;
             margin-bottom: 20px;
             font-weight: 700;
-            font-size: 42px;
+            font-size: 36px;
+            line-height: 43px;
         }
 
         .area-btn {
             display: flex;
-            justify-content: center;
+            justify-content: space-between;
             font-weight: 600;
             font-size: 15px;
 
@@ -550,18 +695,18 @@ body {
                 color: #fff;
                 background: #1D9BF0;
                 border-radius: 100px;
-                width: 110px;
+                width: 165px;
                 height: 38px;
+                text-align: center;
                 margin-left: 8px;
                 line-height: 36px;
-
             }
 
             .withdraw-btn {
                 background: rgba(56, 154, 255, 0.01);
                 border: 1px solid #1D9BF0;
                 box-sizing: border-box;
-                width: 110px;
+                width: 165px;
                 font-size: 15px;
                 height: 38px;
                 text-align: center;
@@ -583,7 +728,6 @@ body {
     .tab-bar {
         display: flex;
         align-items: center;
-        margin-top: 25px;
         position: sticky;
         position: -webkit-sticky;
         top: 0px;
@@ -597,7 +741,7 @@ body {
             justify-content: center;
             padding: 17px 0;
             box-sizing: border-box;
-            border-bottom: 1px solid #d1d1d1;
+            border-bottom: 1px solid #ECECEC;
             cursor: pointer;
 
             .icon {
@@ -624,12 +768,18 @@ body {
             .cell {
                 display: flex;
                 justify-content: space-between;
-                align-items: center;
                 min-height: 66px;
                 box-sizing: border-box;
-                padding-left: 20px;
+                padding-left: 14px;
+                position: relative;
                 cursor: pointer;
 
+                .red-dots {
+                    position: absolute; 
+                    right: 4px; 
+                    top: 4px;
+                }
+
                 .img-wrapper {
                     position: relative;
                     margin-right: 16px;
@@ -646,6 +796,10 @@ body {
                         right: -4px;
                         bottom: 2px;
                     }
+
+                    .icon-big-give {
+                        margin-top: 14px;
+                    }
                 }
 
                 .info-wrapper {
@@ -653,10 +807,9 @@ body {
                     height: 100%;
                     display: flex;
                     justify-content: space-between;
-                    align-items: center;
                     border-bottom: 1px solid #d1d1d1;
                     box-sizing: border-box;
-                    padding: 8px 16px 8px 0;
+                    padding: 10px 14px 10px 0;
 
                     .left {
                         .nickname {
@@ -691,10 +844,11 @@ body {
                                     max-width: 68px;
                                     word-break: break-all;
                                     line-height: 18px;
+                                    color: #E29A2E;
                                 }
 
                                 .coin-type {
-                                    color: #E29A2E;
+                                    margin-left: 3px;
                                 }
 
                                 img {
@@ -707,23 +861,40 @@ body {
                             .desc {
                                 font-size: 12px;
                                 color: #b6b6b6;
-                                display: flex;
-                                align-items: center;
-                                justify-content: end;
                                 margin-top: 5px;
+                                text-align: right;
 
-                                .send-btn {
-                                    height: 24px;
-                                    width: 54px;
-                                    line-height: 24px;
-                                    text-align: center;
-                                    border-radius: 100px;
-                                    border: 1px solid #1D9BF0;
-                                    color: #1D9BF0;
-                                    font-weight: 500;
-                                    font-size: 14px;
-                                    cursor: pointer;
-                                    margin-left: 8px;
+                                .desc-bottom-bar {
+                                    display: flex;
+                                    align-items: center;
+                                    justify-content: end;
+                                    margin-top: 10px;
+
+                                    .btn {
+                                        min-width: 80px;
+                                        height: 29px;
+                                        padding: 0 8px;
+                                        box-sizing: border-box;
+                                        font-weight: 400;
+                                        font-size: 14px;
+                                        cursor: pointer;
+                                        text-align: center;
+                                        border-radius: 100px;
+                                        color: #5E5E5E;
+                                        border: 1px solid #DFDFDF;
+                                        display: flex;
+                                        align-items: center;
+                                        justify-content: center;
+                                    }
+
+                                    .send-btn {
+                                        border: 1px solid #1D9BF0;
+                                        color: #1D9BF0;
+                                    }
+
+                                    .detail-btn, .send-btn {
+                                        margin-left: 8px;
+                                    }
                                 }
                             }
                         }
@@ -732,9 +903,16 @@ body {
                             width: 18px;
                             height: 24px;
                             margin-left: 4px;
+                            margin-right: -5px;
                         }
                     }
                 }
+                .info-center {
+                    align-items: center;
+                }
+            }
+            .cell-center {
+                align-items: center;
             }
 
             .icon-empty {
@@ -767,7 +945,7 @@ body {
                     display: flex;
                     justify-content: space-between;
                     align-items: center;
-                    border-bottom: 1px solid #d1d1d1;
+                    border-bottom: 1px solid #ECECEC;
                     box-sizing: border-box;
                     padding-right: 16px;
 

+ 1 - 1
src/view/popup/top-up/home.vue

@@ -1,7 +1,7 @@
 <template>
     <!-- 公共组件 -->
     <div class="info">
-        <v-head :title="'TopUp'" :show_more="true" :back_url="'/'"></v-head>
+        <v-head :title="'TopUp'" :show_more="true" :show_help="false" :back_url="'/'"></v-head>
         <currency-list style="height: calc(100% - 48px);" @selectCurrency="selectCurrency" :page="'top-up'"></currency-list>
     </div>
 

+ 2 - 2
src/view/popup/transactions.vue

@@ -2,14 +2,14 @@
     <!-- 公共组件 -->
     <div class="info">
         <v-head :title="'Transactions'" :show_more="false" :back_url="'/'" @onBack="clickBack" ></v-head>
-        <options-transactions style="height: calc(100% - 48px);"></options-transactions>
+        <popup-transactions style="height: calc(100% - 48px);"></popup-transactions>
     </div>
 
 </template>
 
 <script setup>
 import VHead from '@/view/popup/components/head.vue'
-import OptionsTransactions from "@/view/components/options-transactions";
+import PopupTransactions from "@/view/components/popup-transactions";
 
 
 </script>

+ 1 - 1
src/view/popup/withdraw/home.vue

@@ -1,7 +1,7 @@
 <template>
     <!-- 公共组件 -->
     <div class="info">
-        <v-head :title="'Withdraw'" :show_more="true" :back_url="'/'"></v-head>
+        <v-head :title="'Withdraw'" :show_more="true" :show_help="true" :back_url="'/'"></v-head>
         <currency-list style="height: calc(100% - 48px);" :filterEmptyBalance="true" @selectCurrency="selectCurrency" ></currency-list>
     </div>
 

+ 18 - 4
src/view/popup/withdraw/info.vue

@@ -59,7 +59,7 @@
       <div class="left">
         <div class="txt">Receive Amount</div>
         <div class="money">{{ state.amount || 0 }} {{ state.currency_code }}</div>
-        <div class="txt"> Network Fee: {{ state.fee_amount }} {{ withdraw_info.token_symbol }} </div>
+        <div class="txt"> Network Fee: <a-tooltip :title="state.fee_amount">{{ numToFixed(state.fee_amount) }}</a-tooltip> {{ withdraw_info.token_symbol }} </div>
       </div>
       <div class="right">
         <div class="btn" @click="clickBtn" :class="{ enter: state.is_enter_state }">Confirm</div>
@@ -100,14 +100,14 @@ const inputWithdrawCalcFee = () => {
         state.fee_amount = res.data.feeAmountValue || 0
         state.amount = res.data.finalAmountValue || 111
         state.is_enter_state = true
-        inputText()
+        inputText('yes')
       }
     }
 
   })
 }
 
-const inputText = () => {
+const inputText = (is_check_input) => {
   if (!state.withdraw_switch) {
     return
   }
@@ -138,7 +138,9 @@ const inputText = () => {
     return
   } else {
     state.error_msg = ''
-    inputWithdrawCalcFee()
+    if (is_check_input != 'yes') {
+      inputWithdrawCalcFee()
+    }
   }
 }
 
@@ -201,6 +203,14 @@ const clickWithdrawalAll = () => {
   state.input_amount = state.balance
   inputText()
 }
+
+const numToFixed = (str) => {
+  if (String(str).indexOf('.') >= 0) {
+    return Number(str).toFixed(4);
+  } else {
+    return str
+  }
+}
 </script>
 
 
@@ -296,6 +306,9 @@ const clickWithdrawalAll = () => {
           padding: 0 16px;
           font-weight: 500;
           font-size: 15px;
+          &::placeholder {
+            color: #B8B8B8;
+          }
         }
 
         input::-webkit-outer-spin-button,
@@ -326,6 +339,7 @@ const clickWithdrawalAll = () => {
 
     .tips {
       margin-top: 12px;
+      margin-bottom: 12px;
       font-size: 13px;
       height: 118;
       overflow: auto;

+ 2 - 2
src/view/popup/withdraw/paypal.vue

@@ -2,14 +2,14 @@
     <!-- 公共组件 -->
     <div class="info">
         <v-head :title="'Withdraw'" :show_more="true"></v-head>
-        <option-withdraw style="height: calc(100% - 48px);" @back="back"></option-withdraw>
+        <popup-withdraw style="height: calc(100% - 48px);" @back="back"></popup-withdraw>
     </div>
 
 </template>
 
 <script setup>
 import VHead from '@/view/popup/components/head.vue'
-import OptionWithdraw from "@/view/components/option-withdraw.vue";
+import PopupWithdraw from "@/view/components/popup-withdraw.vue";
 import { useRouter } from "vue-router";
 
 let router = useRouter()

برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است