소스 검색

Merge branch 'dev_1.1.1' of DeNet/de-net into master

zhangwei 2 년 전
부모
커밋
82e8eb65f0
59개의 변경된 파일3257개의 추가작업 그리고 187개의 파일을 삭제
  1. BIN
      src/assets/img/icon-group-tab-item.png
  2. BIN
      src/assets/img/icon-logo.png
  3. BIN
      src/assets/img/icon-messgae.png
  4. BIN
      src/assets/img/icon-nft-group-entry.png
  5. BIN
      src/assets/img/icon-nft-group-select.png
  6. BIN
      src/assets/img/icon-user.png
  7. 10 0
      src/assets/img/img-pined-guide-new-2.svg
  8. BIN
      src/assets/img/img-pined-guide-new.png
  9. 6 0
      src/assets/svg/icon-celebration.svg
  10. 6 0
      src/assets/svg/icon-group-success.svg
  11. 4 0
      src/assets/svg/icon-joined-group-logo.svg
  12. 3 0
      src/assets/svg/icon-line.svg
  13. 3 0
      src/assets/svg/icon-messgae.svg
  14. 22 0
      src/assets/svg/icon-nft-group-pc.svg
  15. 3 0
      src/assets/svg/icon-nft-member.svg
  16. 3 0
      src/assets/svg/icon-nft-post.svg
  17. 2 0
      src/assets/svg/icon-tweet-loading.svg
  18. 3 0
      src/assets/svg/icon-user.svg
  19. 13 5
      src/entry/background.js
  20. 65 4
      src/entry/content.js
  21. 1 1
      src/http/configAPI.js
  22. 1 0
      src/http/fetch.js
  23. 29 0
      src/http/group.js
  24. 33 0
      src/http/nft.js
  25. 0 4
      src/iframe/bind-tweet.js
  26. 6 0
      src/iframe/group-card.js
  27. 5 0
      src/iframe/joined-group-list.js
  28. 6 0
      src/iframe/nft-group-card.js
  29. 6 0
      src/iframe/nft-group.js
  30. 2 4
      src/iframe/publish.js
  31. 7 0
      src/iframe/tab-group.js
  32. 37 0
      src/logic/background/fetch/twitter.js
  33. 58 19
      src/logic/background/twitter.js
  34. 28 0
      src/logic/content/ParseCard.js
  35. 43 0
      src/logic/content/help/twitter.js
  36. 232 0
      src/logic/content/nft.js
  37. 892 77
      src/logic/content/twitter.js
  38. 8 2
      src/manifest.json
  39. 8 0
      src/router/buy-nft.js
  40. 27 2
      src/uilts/chromeExtension.js
  41. 67 43
      src/uilts/help.js
  42. 112 0
      src/view/components/join-group-finish-dialog.vue
  43. 137 0
      src/view/components/nft-group-list.vue
  44. 0 2
      src/view/iframe/bind-tweet/bind-tweet.vue
  45. 7 2
      src/view/iframe/buy-nft/buy/home.vue
  46. 7 2
      src/view/iframe/buy-nft/buy/open-box.vue
  47. 9 3
      src/view/iframe/buy-nft/buy/pay.vue
  48. 247 0
      src/view/iframe/buy-nft/group/tip.vue
  49. 238 0
      src/view/iframe/group-card/card.vue
  50. 200 0
      src/view/iframe/nft/group-card.vue
  51. 27 0
      src/view/iframe/nft/group.vue
  52. 20 5
      src/view/iframe/publish-tips/publish-tips.vue
  53. 6 3
      src/view/iframe/publish/give-dialog.vue
  54. 1 1
      src/view/iframe/red-packet/red-packet.vue
  55. 125 0
      src/view/iframe/tab-group/joined-group-list.vue
  56. 424 0
      src/view/iframe/tab-group/tab-group.vue
  57. 25 2
      src/view/popup/components/tabbar.vue
  58. 6 6
      src/view/popup/tabbar-page/more/index.vue
  59. 27 0
      src/view/popup/tabbar-page/nft/index.vue

BIN
src/assets/img/icon-group-tab-item.png


BIN
src/assets/img/icon-logo.png


BIN
src/assets/img/icon-messgae.png


BIN
src/assets/img/icon-nft-group-entry.png


BIN
src/assets/img/icon-nft-group-select.png


BIN
src/assets/img/icon-user.png


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 10 - 0
src/assets/img/img-pined-guide-new-2.svg


BIN
src/assets/img/img-pined-guide-new.png


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 6 - 0
src/assets/svg/icon-celebration.svg


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 6 - 0
src/assets/svg/icon-group-success.svg


+ 4 - 0
src/assets/svg/icon-joined-group-logo.svg

@@ -0,0 +1,4 @@
+<svg width="32" height="30" viewBox="0 0 32 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M15.9464 28.5523L16.0002 28.6076L16.0539 28.5523L31.0538 13.1124L31.0987 13.0661L31.0597 13.0147L23.4185 2.95464L23.396 2.925H23.3588H8.64124H8.60403L8.58152 2.95464L0.940275 13.0147L0.901262 13.0661L0.946206 13.1124L15.9464 28.5523ZM28.8923 12.9257L16.0002 26.1958L3.108 12.9257L9.40121 4.64043H22.5988L28.8923 12.9257Z" fill="#1D9BF0" stroke="#1D9BF0" stroke-width="0.15"/>
+<path d="M16.8074 12.7008C17.1973 12.3323 17.5094 11.8893 17.7252 11.398C17.941 10.9068 18.0561 10.3773 18.0639 9.84079C18.0721 9.30434 17.9728 8.77168 17.7716 8.2743C17.5705 7.77691 17.2716 7.32491 16.8928 6.94501C17.2274 6.8184 17.5823 6.75364 17.94 6.75391C19.6031 6.75391 20.9528 8.13541 20.9528 9.84079C20.9528 11.5459 19.6037 12.9277 17.94 12.9277C17.5516 12.9277 17.1661 12.8512 16.8074 12.7008ZM18.2377 13.9567H19.3212C21.4695 13.9567 23.2097 15.741 23.2097 17.9438V18.2019C23.2097 18.899 22.0842 19.0577 20.5271 19.0925C20.6015 18.9758 20.6399 18.841 20.6375 18.702V18.3677C20.6421 17.4897 20.4244 16.6249 20.0048 15.8536C19.5852 15.0824 18.9773 14.4299 18.2377 13.9567ZM13.8241 6.75391C15.4869 6.75391 16.8353 8.13541 16.8353 9.84079C16.8353 11.5459 15.4884 12.9277 13.8241 12.9277C12.1616 12.9277 10.8119 11.5459 10.8119 9.84079C10.8119 8.13541 12.1616 6.75391 13.8241 6.75391ZM12.6944 13.9567H15.2041C17.3527 13.9567 19.0938 15.741 19.0938 17.9438V18.2019C19.0938 19.0687 17.3512 19.1032 15.2041 19.1032H12.6944C10.5461 19.1032 8.80469 19.1014 8.80469 18.2019V17.9438C8.80469 15.7419 10.5458 13.9567 12.6944 13.9567Z" fill="#1D9BF0"/>
+</svg>

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

@@ -0,0 +1,3 @@
+<svg width="70" height="1" viewBox="0 0 70 1" fill="none" xmlns="http://www.w3.org/2000/svg">
+<line opacity="0.4" x1="70" y1="0.25" x2="-2.18557e-08" y2="0.249994" stroke="black" stroke-width="0.5"/>
+</svg>

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

@@ -0,0 +1,3 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9.4 1.5H6.6C5.8646 1.5 5.1364 1.64485 4.45697 1.92627C3.77755 2.2077 3.16021 2.62019 2.6402 3.1402C2.12019 3.66021 1.7077 4.27755 1.42627 4.95697C1.14485 5.6364 1 6.3646 1 7.1C1 11.65 5.9 13.75 9.4 15.15V12.7C10.8852 12.7 12.3096 12.11 13.3598 11.0598C14.41 10.0096 15 8.58521 15 7.1C15 5.61479 14.41 4.19041 13.3598 3.1402C12.3096 2.09 10.8852 1.5 9.4 1.5ZM5 8C5.55228 8 6 7.55228 6 7C6 6.44772 5.55228 6 5 6C4.44772 6 4 6.44772 4 7C4 7.55228 4.44772 8 5 8ZM9 7C9 7.55228 8.55228 8 8 8C7.44772 8 7 7.55228 7 7C7 6.44772 7.44772 6 8 6C8.55228 6 9 6.44772 9 7ZM11 8C11.5523 8 12 7.55228 12 7C12 6.44772 11.5523 6 11 6C10.4477 6 10 6.44772 10 7C10 7.55228 10.4477 8 11 8Z" fill="white"/>
+</svg>

+ 22 - 0
src/assets/svg/icon-nft-group-pc.svg

@@ -0,0 +1,22 @@
+<svg width="505" height="180" viewBox="0 0 505 180" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_19137_186552)">
+<rect width="505" height="180" fill="#48B1F7"/>
+<g opacity="0.1">
+<path d="M409.686 224.444C408.9 225.259 407.595 225.259 406.808 224.444L290.68 104.171C289.996 103.463 289.929 102.362 290.522 101.577L349.387 23.5977C349.765 23.0971 350.356 22.8027 350.983 22.8027H465.509C466.136 22.8027 466.727 23.0971 467.105 23.5978L525.97 101.577C526.563 102.362 526.496 103.463 525.813 104.171L409.686 224.444Z" fill="url(#paint0_linear_19137_186552)"/>
+<path d="M414.632 95.2211C417.711 92.3109 420.175 88.8131 421.879 84.9344C423.583 81.0558 424.492 76.8749 424.553 72.6389C424.619 68.4032 423.834 64.1974 422.246 60.2702C420.658 56.343 418.298 52.7741 415.307 49.7746C417.949 48.7748 420.751 48.2636 423.576 48.2656C436.707 48.2656 447.364 59.1737 447.364 72.6389C447.364 86.1017 436.712 97.0121 423.576 97.0121C420.508 97.0121 417.465 96.4081 414.632 95.2211V95.2211ZM425.926 105.137H434.481C451.444 105.137 465.184 119.225 465.184 136.618V138.656C465.184 144.16 456.297 145.413 444.003 145.688C444.59 144.767 444.893 143.702 444.874 142.604V139.965C444.91 133.033 443.191 126.204 439.878 120.115C436.565 114.025 431.766 108.873 425.926 105.137ZM391.077 48.2656C404.206 48.2656 414.853 59.1737 414.853 72.6389C414.853 86.1017 404.218 97.0121 391.077 97.0121C377.95 97.0121 367.294 86.1017 367.294 72.6389C367.294 59.1737 377.95 48.2656 391.077 48.2656ZM382.157 105.137H401.973C418.938 105.137 432.686 119.225 432.686 136.618V138.656C432.686 145.5 418.927 145.773 401.973 145.773H382.157C365.195 145.773 351.445 145.759 351.445 138.656V136.618C351.445 119.232 365.193 105.137 382.157 105.137V105.137Z" fill="url(#paint1_linear_19137_186552)"/>
+</g>
+</g>
+<defs>
+<linearGradient id="paint0_linear_19137_186552" x1="350.387" y1="16.9273" x2="520.098" y2="243.64" gradientUnits="userSpaceOnUse">
+<stop stop-color="white"/>
+<stop offset="0.867568" stop-color="white" stop-opacity="0"/>
+</linearGradient>
+<linearGradient id="paint1_linear_19137_186552" x1="394.004" y1="76.9518" x2="454.893" y2="168.5" gradientUnits="userSpaceOnUse">
+<stop offset="0.14461" stop-color="#1372B3"/>
+<stop offset="1" stop-color="#5BBAFA" stop-opacity="0"/>
+</linearGradient>
+<clipPath id="clip0_19137_186552">
+<rect width="505" height="180" fill="white"/>
+</clipPath>
+</defs>
+</svg>

+ 3 - 0
src/assets/svg/icon-nft-member.svg

@@ -0,0 +1,3 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M8 8C9.69225 8 11.0625 6.62975 11.0625 4.9375C11.0625 3.24525 9.69225 1.875 8 1.875C7.59779 1.87489 7.19951 1.95402 6.8279 2.10788C6.45628 2.26175 6.11863 2.48733 5.83423 2.77173C5.54983 3.05613 5.32425 3.39378 5.17038 3.7654C5.01652 4.13701 4.93739 4.53529 4.9375 4.9375C4.9375 6.62975 6.30775 8 8 8ZM8 8.875C5.956 8.875 1.875 10.0475 1.875 12.375V14.125H14.125V12.375C14.125 10.0475 10.044 8.875 8 8.875Z" fill="white"/>
+</svg>

+ 3 - 0
src/assets/svg/icon-nft-post.svg

@@ -0,0 +1,3 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9.4 1.5H6.6C5.8646 1.5 5.1364 1.64485 4.45697 1.92627C3.77755 2.2077 3.16021 2.62019 2.6402 3.1402C2.12019 3.66021 1.7077 4.27755 1.42627 4.95697C1.14485 5.6364 1 6.3646 1 7.1C1 11.65 5.9 13.75 9.4 15.15V12.7C10.8852 12.7 12.3096 12.11 13.3598 11.0598C14.41 10.0096 15 8.58521 15 7.1C15 5.61479 14.41 4.19041 13.3598 3.1402C12.3096 2.09 10.8852 1.5 9.4 1.5ZM5 8C5.55228 8 6 7.55228 6 7C6 6.44772 5.55228 6 5 6C4.44772 6 4 6.44772 4 7C4 7.55228 4.44772 8 5 8ZM9 7C9 7.55228 8.55228 8 8 8C7.44772 8 7 7.55228 7 7C7 6.44772 7.44772 6 8 6C8.55228 6 9 6.44772 9 7ZM11 8C11.5523 8 12 7.55228 12 7C12 6.44772 11.5523 6 11 6C10.4477 6 10 6.44772 10 7C10 7.55228 10.4477 8 11 8Z" fill="white"/>
+</svg>

+ 2 - 0
src/assets/svg/icon-tweet-loading.svg

@@ -0,0 +1,2 @@
+<svg height="100%" viewBox="0 0 32 32" width="100%" xmlns="http://www.w3.org/2000/svg">
+<circle cx="16" cy="16" fill="none" r="14" stroke-width="4" style="stroke: rgb(29, 155, 240); opacity: 0.2;"></circle><circle cx="16" cy="16" fill="none" r="14" stroke-width="4" style="stroke: rgb(29, 155, 240); stroke-dasharray: 80; stroke-dashoffset: 60;"></circle></svg>

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

@@ -0,0 +1,3 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M8 8C9.69225 8 11.0625 6.62975 11.0625 4.9375C11.0625 3.24525 9.69225 1.875 8 1.875C7.59779 1.87489 7.19951 1.95402 6.8279 2.10788C6.45628 2.26175 6.11863 2.48733 5.83423 2.77173C5.54983 3.05613 5.32425 3.39378 5.17038 3.7654C5.01652 4.13701 4.93739 4.53529 4.9375 4.9375C4.9375 6.62975 6.30775 8 8 8ZM8 8.875C5.956 8.875 1.875 10.0475 1.875 12.375V14.125H14.125V12.375C14.125 10.0475 10.044 8.875 8 8.875Z" fill="white"/>
+</svg>

+ 13 - 5
src/entry/background.js

@@ -18,7 +18,9 @@ import {
     injectExtensionPopup,
     setPopupConfig,
     windwoLoadSetPopupPage,
-    setActionPopup
+    setActionPopup,
+    getTwitterNftPostPre,
+    nftTxtPublish
 } from "@/logic/background/twitter";
 
 import { PingPong } from "@/logic/background/help";
@@ -59,11 +61,11 @@ chrome.alarms.onAlarm.addListener(function (alarm) {
     }
 });
 
-chrome.action.onClicked.addListener(function(tab) { 
+chrome.action.onClicked.addListener(function (tab) {
     injectExtensionPopup(tab);
 });
 
-chrome.tabs.onActivated.addListener(function(activeInfo) {
+chrome.tabs.onActivated.addListener(function (activeInfo) {
     setPopupConfig(activeInfo);
 })
 
@@ -71,7 +73,7 @@ function onInstalledMethod() {
     onInstalledCreateTab()
     onInstalledMid()
     onInstalledUserSet()
-    
+
     // pingpang
     chrome.alarms.create('PingPong', {
         //1分鐘之後開始(該值不能小於1) 
@@ -126,12 +128,18 @@ function onMessageMethod(req, sender, sendResponse) {
             case 'CONTENT_PONG':
                 console.log('CONTENT_PONG')
                 break
-            case 'CONTENT_WINDOW_LOADED_SET_POPUP_PAGE': 
+            case 'CONTENT_WINDOW_LOADED_SET_POPUP_PAGE':
                 // windwoLoadSetPopupPage(req, sender);
                 break;
             case 'CONTENT_SET_POPUP_CONFIG':
                 setActionPopup(req, sender);
                 break;
+            case 'CONTENT_GET_TWITTER_NFT_POST_PRE':
+                getTwitterNftPostPre(req.data, sender)
+                break
+            case 'CONTENT_NFT_TXT_PUBLISH':
+                nftTxtPublish(req.data, sender)
+                break
         }
     }
 }

+ 65 - 4
src/entry/content.js

@@ -24,9 +24,27 @@ import {
     showBuyNFT,
     hideBuyNFT,
     showPopupPage,
-    setPopupConfByPopupPage
+    setPopupConfByPopupPage,
+    loginSuccessHandle,
+    showJoinDialog,
+    showTwitterPost,
+    setTwitterTextarea,
+    showGroupTip,
+    setTabGroupIframeStyle,
+    pageJumpHandler,
+    getTweetProfileNavTop,
+    selectGroupTab,
+    setGroupInfo,
+    refreshTabGroup,
+    groupTipsSelectGroupTab
 } from "@/logic/content/twitter.js";
 
+import { 
+    hideNFTGroupList,
+    setNFTGroupContent,
+    setJoinedGroupIframeStyle
+} from '@/logic/content/nft';
+
 import {
     initFacebookContent
 } from "@/logic/content/facebook.js"
@@ -86,7 +104,6 @@ window.onmessage = (res) => {
 
 
 chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
-    sendResponse('')
     switch (req.actionType) {
         case 'BG_SHOW_PIN_TIPS':
             showPinTips()
@@ -104,12 +121,18 @@ chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
         case 'IFRAME_NFT_SHOW_SALE':
             showNFTSale()
             break
+        case 'IFRAME_NFT_GROUP_LIST_HIDE':
+            hideNFTGroupList()
+            break;
+        case 'IFRAME_NFT_GROUP_SET_CONTENT':
+            setNFTGroupContent(req.publishRes);
+            break;
         case "IFRAME_TWITTER_PUBLISH":
             twitterPublishHandler(req.publishRes);
             break;
         case 'IFRAME_TWITTER_SHOW_POPUP_PAGE':
-            let {from = ''} = req.data || {};
-            showPopupPage({path: '/NFT', from}); 
+            let {from = '' ,showJoinGroupFinish} = req.data || {};
+            showPopupPage({path: '/NFT', from,showJoinGroupFinish }); 
             break
         case "IFRAME_TWITTER_SHOW_BUY_NFT":
             showBuyNFT(req.data)
@@ -120,6 +143,44 @@ chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
         case 'BG_SET_POPUP_CONFIG':
             setPopupConfByPopupPage();
             break
+        case 'IFREME_TAB_GROUP_SET_IFRAME_HEIGHT':
+            console.log('IFREME_TAB_GROUP_SET_IFRAME_HEIGHT',req)
+            setTabGroupIframeStyle(req.data);
+            break
+        case 'IFREME_TAB_GROUP_CONTENT_GET_NAV_TOP':
+            getTweetProfileNavTop(req.data);
+        case 'IFRAME_PAGE_JUMP':
+            pageJumpHandler(req.data);
+            break;
+        case 'BG_LOGIN_SET_USERINFO_CB':
+            loginSuccessHandle();
+            break;
+        case 'IFRAME_SHOW_JOIN_DIALOG':
+            console.log('IFRAME_SHOW_JOIN_DIALOG')
+            showJoinDialog(req.data)
+            break
+        case 'IFRAME_SHOW_GROUP_TIP':
+            showGroupTip()
+            break
+        case 'IFRAME_SHOW_POST_DIALOG':
+            console.log('IFRAME_SHOW_POST_DIALOG')
+            showTwitterPost(req.data)
+            break
+        case 'BACK_TWITTER_NFT_POST_PRE':
+            setTwitterTextarea(req.data)
+            break
+        case 'SWITCH_GROUP_STATUS':
+            groupTipsSelectGroupTab(req.data);
+            break
+        case 'IFRAME_GROUP_BANNER_GROUP_INFO':
+            setGroupInfo(req.data)
+            break;
+        case 'IFRAME_JOINED_GROUP_SET_STYLE':
+            setJoinedGroupIframeStyle(req.data)
+            break
+        case 'BACK_NFT_PUBLISH_DONE':
+            refreshTabGroup()
+            break
     }
 })
 

+ 1 - 1
src/http/configAPI.js

@@ -1,4 +1,4 @@
-export const appVersionCode = 10
+export const appVersionCode = 11
 
 const api = {
 	production: 'https://api.denetme.net',

+ 1 - 0
src/http/fetch.js

@@ -20,6 +20,7 @@ export async function commonFetch({ url, method = 'POST' , params = {}, baseInfo
         let _url = baseAPIUrl + url
         fetch(_url, {
             method: method, // or 'PUT'
+            cache: 'no-cache',
             headers: {
                 'Content-Type': 'application/json',
             },

+ 29 - 0
src/http/group.js

@@ -0,0 +1,29 @@
+import {
+  service
+} from "./request";
+
+export function getTwitterNftGroupInfo(params) {
+  return service({
+    url: `/nft/group/getTwitterNftGroupInfo`,
+    method: 'post',
+    data: params
+  })
+}
+
+
+export function getTwitterNftPostPre(params) {
+  return service({
+    url: `/nft/group/post/pre`,
+    method: 'post',
+    data: params
+  })
+
+}
+
+export function setGroupJoin(params) {
+  return service({
+    url: `/nft/group/join`,
+    method: 'post',
+    data: params
+  })
+}

+ 33 - 0
src/http/nft.js

@@ -39,3 +39,36 @@ export function getNftProjectInfo(params) {
         data: params
     })
 }
+
+export function listJoinNftGroup(params) {
+    return service({
+        url: `/nft/group/listJoinNftGroup`,
+        method: 'post',
+        data: params
+    })
+}
+
+export function prePost(params) {
+    return service({
+        url: `/nft/group/post/pre`,
+        method: 'post',
+        data: params
+    })
+}
+
+
+export function getGroupPostList(params) {
+    return service({
+        url: `/nft/group/post/list`,
+        method: 'post',
+        data: params
+    })
+}
+
+export function getTwitterNftGroupInfo(params) {
+    return service({
+        url: `/nft/group/getTwitterNftGroupInfo`,
+        method: 'post',
+        data: params
+    })
+}

+ 0 - 4
src/iframe/bind-tweet.js

@@ -1,10 +1,6 @@
 import { createApp } from 'vue'
 import App from '@/view/iframe/bind-tweet/bind-tweet.vue'
-import ElementPlus from 'element-plus'
-import 'element-plus/dist/index.css'
-
 
 const app = createApp(App);
 
-app.use(ElementPlus);
 app.mount('#app');

+ 6 - 0
src/iframe/group-card.js

@@ -0,0 +1,6 @@
+import { createApp } from 'vue'
+import App from '@/view/iframe/group-card/card.vue'
+
+const app = createApp(App);
+
+app.mount('#app');

+ 5 - 0
src/iframe/joined-group-list.js

@@ -0,0 +1,5 @@
+import { createApp } from 'vue'
+import App from '@/view/iframe/tab-group/joined-group-list.vue'
+
+const app = createApp(App);
+app.mount('#app');

+ 6 - 0
src/iframe/nft-group-card.js

@@ -0,0 +1,6 @@
+import { createApp } from 'vue'
+import App from '@/view/iframe/nft/group-card.vue'
+
+const app = createApp(App);
+
+app.mount('#app');

+ 6 - 0
src/iframe/nft-group.js

@@ -0,0 +1,6 @@
+import { createApp } from 'vue'
+import App from '@/view/iframe/nft/group.vue'
+
+const app = createApp(App);
+
+app.mount('#app');

+ 2 - 4
src/iframe/publish.js

@@ -1,11 +1,9 @@
 import { createApp } from 'vue'
 import App from '@/view/iframe/publish/publish.vue'
-import ElementPlus from 'element-plus'
-import 'element-plus/dist/index.css'
 
 import "ant-design-vue/dist/antd.css"; // or 'ant-design-vue/dist/antd.less'
 
-import {Button,message,Tooltip} from "ant-design-vue";
+import {Button,message,Tooltip, Switch} from "ant-design-vue";
 
 message.config({
     top: `10px`,
@@ -18,5 +16,5 @@ const app = createApp(App);
 app.use(Button);
 app.use(Tooltip);
 app.use(message);
-app.use(ElementPlus);
+app.use(Switch);
 app.mount('#app');

+ 7 - 0
src/iframe/tab-group.js

@@ -0,0 +1,7 @@
+import { createApp } from 'vue'
+import App from '@/view/iframe/tab-group/tab-group.vue'
+import ElementPlus from 'element-plus'
+import 'element-plus/dist/index.css'
+
+const app = createApp(App);
+app.mount('#app');

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

@@ -9,6 +9,7 @@ export async function fetchTtwitterRequestToken() {
     })
 }
 
+
 export async function fetchTwitterLogin(oauthToken, consumerKey, oauthVerifier, receivedIds = []) {
     return commonFetch({
         url: '/user/twitterLogin',
@@ -75,4 +76,40 @@ export async function getDiscordUserInfo(params = {}) {
         },
         params
     })
+}
+
+
+export async function fetchGetTwitterNftPostPre(params = {}) {
+    const { accessToken: token = '', uid = '' } = await getChromeStorage('userInfo') || {}
+    if (!token) {
+        return new Promise(function (resolve, reject) {
+            resolve({});
+        })
+    }
+    return commonFetch({
+        url: '/nft/group/post/pre',
+        baseInfo: {
+            token,
+            uid
+        },
+        params
+    })
+}
+
+
+export async function fetchPublish(params = {}) {
+    const { accessToken: token = '', uid = '' } = await getChromeStorage('userInfo') || {}
+    if (!token) {
+        return new Promise(function (resolve, reject) {
+            resolve({});
+        })
+    }
+    return commonFetch({
+        url: '/post/publish',
+        baseInfo: {
+            token,
+            uid
+        },
+        params
+    })
 }

+ 58 - 19
src/logic/background/twitter.js

@@ -1,4 +1,4 @@
-import { fetchTtwitterRequestToken, fetchTwitterLogin, fetchTwitterShortUrl, fetchAllMessageInfo, fetchReadTaskAllMsg, getDiscordUserInfo } from '@/logic/background/fetch/twitter.js'
+import { fetchTtwitterRequestToken, fetchTwitterLogin, fetchTwitterShortUrl, fetchAllMessageInfo, fetchReadTaskAllMsg, getDiscordUserInfo, fetchGetTwitterNftPostPre, fetchPublish } 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'
@@ -274,9 +274,31 @@ export function onInstalledCreateTab() {
                     });
                     removeChromeCookie(nftParams)
                 } else {
-                    chrome.tabs.create({
-                        url: "https://twitter.com",
-                    });
+                    let nftGroupParams = {
+                        name: 'nft_group_info',
+                        url: pageUrl
+                    }
+                    getChromeCookie(nftGroupParams, (res) => {
+                        let { twitterAccount } = res;
+                        if (res && twitterAccount) {
+                            // setChromeStorage({ groupTabData: JSON.stringify({
+                            //     deTabVal: 'deGroupTab'
+                            // })})
+                            chrome.storage.local.set({ groupTabData: JSON.stringify({
+                                deTabVal: 'deGroupTab'
+                            })}, (res)=> {
+                                let url = `https://twitter.com/${twitterAccount}`
+                                chrome.tabs.create({
+                                    url
+                                });
+                            })
+                            removeChromeCookie(nftGroupParams)
+                        } else {
+                            chrome.tabs.create({
+                                url: "https://twitter.com",
+                            });
+                        }
+                    })
                 }
             })
         }
@@ -378,16 +400,16 @@ export const setPopupConfig = (activeInfo) => {
         active: true,
         currentWindow: true
     }, (tabs) => {
-        if(tabs.length) {
-            let {pendingUrl = '', url = ''} = tabs[0];
-            if(pendingUrl.startsWith('https://twitter.com') || url.startsWith('https://twitter.com')) {
+        if (tabs.length) {
+            let { pendingUrl = '', url = '' } = tabs[0];
+            if (pendingUrl.startsWith('https://twitter.com') || url.startsWith('https://twitter.com')) {
                 sendActivetabMessage({
                     actionType: 'BG_SET_POPUP_CONFIG'
                 });
             } else {
                 chrome.action.setPopup({
                     popup: 'popup.html',
-                },function() {
+                }, function () {
                 });
             }
 
@@ -407,37 +429,54 @@ export const setPopupConfig = (activeInfo) => {
 }
 
 export const windwoLoadSetPopupPage = (data, sender) => {
-    let {url = ''} = sender.tab;
-    if(url.startsWith('chrome://')) {
+    let { url = '' } = sender.tab;
+    if (url.startsWith('chrome://')) {
         chrome.action.setPopup({
             popup: 'popup.html',
-        },function() {
+        }, function () {
         });
     } else {
         chrome.action.setPopup({
             popup: '',
-        },function() {
+        }, function () {
         });
     }
 }
 
 export const setActionPopup = (data) => {
-    let {popup} = data.data || {};
-    if(popup) {
+    let { popup } = data.data || {};
+    if (popup) {
         chrome.action.getPopup(
             {},
-            function(result){
-                if(!result) {
+            function (result) {
+                if (!result) {
                     chrome.action.setPopup({
                         popup,
-                    },function() {
+                    }, function () {
                     });
                 }
-        });
+            });
     } else {
         chrome.action.setPopup({
             popup: '',
-        },function() {
+        }, function () {
         });
     }
+}
+
+export const getTwitterNftPostPre = (params, sender) => {
+    fetchGetTwitterNftPostPre(params).then((res) => {
+        if(res.code == 0){
+            chrome.tabs.sendMessage(sender.tab.id, { actionType: 'BACK_TWITTER_NFT_POST_PRE', data: res.data }, (res) => { console.log(res) });
+        }
+    })
+}
+
+
+export const nftTxtPublish = (params,sender) => {
+    fetchPublish(params).then((res) => {
+        if(res.code == 0){
+            chrome.tabs.sendMessage(sender.tab.id, { actionType: 'BACK_NFT_PUBLISH_DONE', data: res.data }, (res) => { console.log(res) });
+        }
+    })
 }

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

@@ -257,6 +257,13 @@ class ParseCard {
         _iframe.style.cssText = 'border:medium none; width:375px; min-height:300px;'
         return _iframe
     }
+    createNftGroupIframe({ project_Id, tweet_Id}) {
+        let _iframe = document.createElement('iframe')
+        _iframe.id = project_Id
+        _iframe.src = chrome.runtime.getURL('/iframe/nft-group-card.html') + `?projectId=${project_Id}&tweet_Id=${tweet_Id}`;
+        _iframe.style.cssText = 'border:medium none; width:505px; min-height:180px;'
+        return _iframe
+    }
     isHasIframeByArticle(dom_card) {
         if (!dom_card || !dom_card.parentElement) {
             return
@@ -402,5 +409,26 @@ class ParseCard {
             dom.appendChild(this.createIframe({ post_Id, tweet_author }, true))
         }
     }
+    replaceNftGroupDomRedPacket({ dom_card, tweet_Id, post_Id, time, short_url }) {
+        if (!dom_card || !dom_card.parentElement) {
+            return
+        }
+        let dom = dom_card.querySelector('div[aria-labelledby]')
+        if (dom) {
+            for (let i = 0; i < dom.childNodes.length; i++) {
+                if (dom.children[i].tagName.toLowerCase() != 'iframe') {
+                    dom.children[i].style.display = 'none'
+                }
+            }
+        } else {
+            dom = dom_card.querySelector('div[lang][dir=auto]').parentElement
+        }
+
+        dom.style = 'min-height:180px'
+        if (dom) {
+            let project_Id = post_Id.replace('nft_group/', '');
+            dom.appendChild(this.createNftGroupIframe({ project_Id,tweet_Id }))
+        }
+    }
 }
 export default new ParseCard()

+ 43 - 0
src/logic/content/help/twitter.js

@@ -0,0 +1,43 @@
+
+// 根据提示dom 跳转到推文详情页面
+export const jumpTwitterDetailByAlert = () => {
+    let num = 10
+    let timer = setInterval(() => {
+        if (num <= 0) {
+            clearInterval(timer)
+            return
+        }
+        let alert = document.querySelector('div[role=alert]')
+        if (alert) {
+            let a = alert.querySelector('a')
+            if (a) {
+                clearInterval(timer)
+                a.click()
+            }
+        }
+        num--
+    }, 500)
+}
+
+export const showEditTweet = (callback) => {
+    let bigBtn = document.querySelector('a[data-testid="SideNav_NewTweet_Button"]');
+    if (bigBtn) {
+        bigBtn.click();
+    } else {
+        let smallBtn = document.querySelector('a[href="/compose/tweet"]')
+        smallBtn && smallBtn.click();
+    }
+    let num = 10
+    let timer = setInterval(() => {
+        if(num <= 0){
+            clearInterval(timer)
+            return
+        }
+        let inputEle = document.querySelector('div[contenteditable="true"]');
+        if(inputEle){
+            clearInterval(timer)
+            callback && callback()
+        }
+        num-- 
+    }, 500);
+}

+ 232 - 0
src/logic/content/nft.js

@@ -0,0 +1,232 @@
+import { getOffsetRect, nextTick } from '@/uilts/help'
+import { listJoinNftGroup } from '@/http/nft';
+import { getChromeStorage } from '@/uilts/chromeExtension.js'
+import { _setPublishContent, publishNFTTweetPost, bindTwitterArt, bindTwitterArtMethod } from './twitter';
+import { jumpTwitterDetailByAlert } from '@/logic/content/help/twitter.js'
+
+var ifShowNftGroup = false;
+var tempNftGroupPost = null;
+
+export const showNFTGroupIcon = () => {
+    let urlInfo = new URL(window.location.href)
+    let isTwitter = urlInfo.hostname === 'twitter.com'
+    let toolElem = document.querySelector('div[data-testid="toolBar"]');
+    let isAppend = toolElem && toolElem.querySelector('#de-nft-group-enter');
+    let where = isTwitter && toolElem && !isAppend && ifShowNftGroup;
+    if (where) {
+        let oDiv = document.createElement(`div`);
+            oDiv.id = 'de-nft-group-enter';
+        let oImg = document.createElement('img');
+            oImg.src = require("@/assets/img/icon-nft-group-entry.png");
+            oImg.className = 'addGroup';
+            oDiv.innerHTML = `
+                ${oImg.outerHTML}
+                <style>
+                    #de-nft-group-enter {position:relative; display:flex; padding:0 8px;}
+                    #de-nft-group-enter .addGroup {cursor:pointer; height:32px;}
+                </style>
+            `;
+            oDiv.addEventListener('click', (e) => {
+                showNFTGroupList(e);
+                e.stopPropagation();
+            })
+        toolElem.firstChild.appendChild(oDiv)
+    }
+}
+
+export const showNFTGroupList = (e) => {
+    let rectObject = e.target.getBoundingClientRect();
+    let { top, left, height } = rectObject;
+    let oTop = top + height + 10;
+    // 居底判断
+    let wHeight = document.body.offsetHeight || document.body.clientHeight;
+    if ((top + height + 290) > wHeight) oTop = top - 290;
+    let iframe = document.createElement('iframe');
+        iframe.src = chrome.runtime.getURL(`/iframe/nft-group.html`)
+        iframe.style.cssText = 'border:medium none; width:315px; height:260px;';
+    let html = document.createElement('div');
+        html.id = 'de-nft-group-list';
+        html.innerHTML = `
+            <div class="de-nft-group-div">
+                ${iframe.outerHTML}
+            </div>
+            <div class="de-nft-group-mask"></div>
+            <style>
+                .de-nft-group-div {position:fixed; display:flex; align-items:center; justify-content:center; z-index:100; width:350px; height:280px; left:${left}px; top:${oTop}px; background:#fff; border-radius:20px; box-shadow:0px 4px 20px 0px rgba(0, 0, 0, 0.26);}
+                .de-nft-group-mask {position:fixed; z-index:99; width:100%; height:100%; left:0; top:0;}
+            </style>`;
+
+    document.body.appendChild(html);
+    document.querySelector('.de-nft-group-mask').addEventListener('click', () => {
+        hideNFTGroupList();
+    })
+}
+
+export const hideNFTGroupList = () => {
+    let dom = document.querySelector('#de-nft-group-list');
+    dom && document.body.removeChild(dom);
+}
+
+export const checkUserJoinGroup = (fn) => {
+    getChromeStorage('userInfo', (res) => {
+        // 如果登录
+        if (res) {
+            listJoinNftGroup({
+                params: {
+                    pageNum: 1,
+                    pageSize: 1
+                }
+            }).then(res => {
+                let { data = [] } = res;
+                if (data !== null && data.length > 0) {
+                    ifShowNftGroup = true;
+                    if (fn) fn()
+                }
+            })
+        }
+    })
+}
+
+export const clearPostContent = (fn) => {
+    let edit = document.querySelector('div[contenteditable="true"]')
+    let where = (edit.innerText === '\n') || (edit.innerText === '')
+    if (where) {
+        fn()
+    } else {
+        nextTick(() => {
+            let inputEle = document.querySelector('div[contenteditable="true"]');
+            if (inputEle) {
+                inputEle.focus();
+            }
+        }, 20).then(() => {
+            document.execCommand('selectAll');
+            document.execCommand('delete');
+            clearPostContent(fn)
+        })
+    }
+}
+
+export const setPostContent = (res) => {
+    nextTick(() => {
+        let inputEle = document.querySelector('div[contenteditable="true"]');
+        if (inputEle) {
+            inputEle.focus();
+        }
+    }, 100).then(() => {
+        _setPublishContent(res.srcContent + ' ');
+    })
+}
+
+export const endPostContent = () => {
+    return new Promise((resolve) => {
+        let inputEle = document.querySelector('div[contenteditable="true"]');
+        let range = document.createRange();
+            range.selectNodeContents(inputEle);
+            range.collapse(false);
+        let sel = window.getSelection();
+            sel.removeAllRanges();
+            sel.addRange(range);
+        resolve()
+    })
+}
+
+export const setNFTGroupContent = (res) => {
+    tempNftGroupPost = res;
+
+    let dialogDiv = document.querySelectorAll('div[role="dialog"]');
+    if (dialogDiv.length === 0) {
+        let bigBtn = document.querySelector('a[data-testid="SideNav_NewTweet_Button"]');
+        if (bigBtn) {
+            bigBtn.click();
+        } else {
+            let smallBtn = document.querySelector('a[href="/compose/tweet"]')
+            smallBtn && smallBtn.click();
+        }
+    }
+
+    let edit = document.querySelector('div[contenteditable="true"]')
+    let where = (edit.innerText === '\n') || (edit.innerText === '')
+    if (where) {
+        setPostContent(res)
+        nextTick(() => {
+            _addTweetButtonListen()
+        }, 100)
+        
+    } else {
+        endPostContent().then(() => {
+            let inputEle = document.querySelector('div[contenteditable="true"]');
+            if (inputEle) {
+                inputEle.focus();
+            }
+            setPostContent(res)
+            nextTick(() => {
+                _addTweetButtonListen()
+            }, 100)
+        })
+    }
+}
+
+export const elemAddEventListener = (elem, action, fn) => {
+    if (elem) {
+        elem.addEventListener(action, fn)
+    }
+}
+
+
+export const addJoinedGroupList = () => {
+    if(ifShowNftGroup) {
+        let {pathname} = window.location;
+
+        let iframe = document.createElement('iframe');
+            iframe.id = 'de-joined-group-list';
+            iframe.src = chrome.runtime.getURL('/iframe/joined-group-list.html');
+            iframe.style.cssText = `border: medium none;height: 120px;border-radius: 16px;margin-bottom: 16px`
+
+        let iframeContent = document.getElementById('de-joined-group-list');
+
+        if (!iframeContent && pathname == '/home') {
+            let sidebarColumn = document.querySelector('div[data-testid="sidebarColumn"]');
+            if(sidebarColumn) {
+                let searchDom = sidebarColumn.querySelector('form[role="search"]');
+                if(searchDom) {
+                    let listWrapperDom = searchDom.parentElement.parentElement.parentElement.parentElement;
+                    if(listWrapperDom) {
+                        let listParent = listWrapperDom.parentElement;
+                        if(listParent) {
+                            listParent.insertBefore(iframe, listWrapperDom.nextElementSibling.nextElementSibling);
+                        }
+                    }
+                }
+            } 
+        }
+    }
+};
+
+export const setJoinedGroupIframeStyle = (params) => {
+    let {height = '321px'} = params;
+    let iframeContent = document.getElementById('de-joined-group-list');
+    if(iframeContent) {
+        iframeContent.style.height = height;
+    }
+}
+
+
+function _addTweetButtonListen() {
+    let btn = document.querySelector('div[data-testid="tweetButton"]');
+
+    btn.removeEventListener('click', _postTweetContent);
+    btn.addEventListener('click', _postTweetContent)
+}
+function _postTweetContent() {
+    if (tempNftGroupPost && tempNftGroupPost.groupId) {
+        publishNFTTweetPost(tempNftGroupPost)
+        bindTwitterArt.needBind = true;
+        bindTwitterArt.postId = tempNftGroupPost.postId;
+        bindTwitterArtMethod();
+        // 非首页处理
+        let homeTag = document.querySelectorAll('div[data-testid="toolBar"]');
+        if (homeTag.length === 1) {
+            jumpTwitterDetailByAlert()
+        }
+    }
+}

+ 892 - 77
src/logic/content/twitter.js

@@ -1,13 +1,35 @@
 import { getChromeStorage, setChromeStorage } from '@/uilts/chromeExtension.js'
-import { throttle, getQueryString, getCookie, nextTick } from '@/uilts/help'
+import { throttle, getQueryString, getCookie, nextTick, getQueryStringByUrl } from '@/uilts/help'
 import { discordAuthRedirectUri } from '@/http/configAPI'
 import { reportSrcPublishEvent } from '@/http/publishApi'
 import Report from "@/log-center/log"
 import { fetchAddFinishEvent } from '@/logic/background/fetch/facebook';
+import { showNFTGroupIcon, hideNFTGroupList, checkUserJoinGroup, elemAddEventListener, addJoinedGroupList } from '@/logic/content/nft';
+import { getTwitterNftGroupInfo } from '@/http/nft'
+import { jumpTwitterDetailByAlert, showEditTweet } from '@/logic/content/help/twitter.js'
+import { clearPostContent } from '@/logic/content/nft.js'
 
 let dom = {};
 
+let tweetAccountBindGroupInfo = {
+    isBind: false,
+    groupInfo: null,
+    isInit: false
+}
+
+let systemInfo = {
+    theme: 'light'
+}
+
+let fixProfileTabAutoTimer = null;
+
+
+let pin_login = false
 function twitterPinLogin() {
+    if(pin_login){
+        return
+    }
+    pin_login = true
     if (window.location.href == 'https://api.twitter.com/oauth/authorize') {
         let code = document.querySelector('code')
 
@@ -177,7 +199,7 @@ function getUserInfo(cb) {
 }
 
 // 绑定推文id所需参数
-let bindTwitterArt = {
+export let bindTwitterArt = {
     needBind: false,
     postId: '',
     isBindIng: false
@@ -313,7 +335,7 @@ function _addDeNetBtn() {
         let innerDeIcon = document.getElementById('de-btn1');
         if (!innerDeIcon) {
             let dialogScheduleBtn = _getScheduleDom(false);
-            _addDeNetEditBtn(dialogScheduleBtn, dom.deBtn1);
+            dom && dom.deBtn1 && _addDeNetEditBtn(dialogScheduleBtn, dom.deBtn1);
         }
     }, 800)
 }
@@ -367,7 +389,12 @@ function addPublishTipsIframe(params = {}) {
 
             let iframe = document.createElement('iframe');
             iframe.id = 'de-publish-tips'
-            iframe.src = chrome.runtime.getURL('/iframe/publish-tips.html');
+            if (params.type == 'nft') {
+                iframe.src = chrome.runtime.getURL('/iframe/publish-tips.html?type="nft"');
+            } else {
+                iframe.src = chrome.runtime.getURL('/iframe/publish-tips.html');
+            }
+
             iframe.style.cssText = `border: medium none; width:270px;height:500px;position: fixed; right: ${right}px; top: 5%;z-index: -1`
             let iframeContent = document.getElementById('de-publish-tips');
             if (!iframeContent) {
@@ -416,7 +443,7 @@ function _deNetBtnClick() {
             if (loadIcon) {
                 return;
             }
-            dom.deBtn.insertBefore(dom.loadingImg, dom.deBtn.querySelector('span'));
+            dom && dom.deBtn && dom.deBtn.insertBefore(dom.loadingImg, dom.deBtn.querySelector('span'));
             setTimeout(() => {
                 dom.loadingImg.style.transform = 'rotate(1080deg)'
             });
@@ -436,7 +463,7 @@ function _deNetBtnClick() {
  * @private
  */
 let isSetContent = false;
-const _setPublishContent = throttle(function (content, time = 1000) {
+export const _setPublishContent = throttle(function (content, time = 1000) {
     if (!isSetContent) {
         isSetContent = true;
         let inputEle = document.querySelector('div[contenteditable="true"]');
@@ -541,7 +568,7 @@ function addSliderNavDeBtn(isSmall = false) {
         let bigDom = document.querySelector('a[href="/compose/tweet"]').parentNode.parentNode;
         let deBtn = document.getElementById('de-btn');
         if (bigDom && !deBtn) {
-            bigDom.appendChild(dom.deBtn);
+            dom && dom.deBtn && bigDom.appendChild(dom.deBtn);
             Report.reportLog({
                 pageSource: Report.pageSource.mainPage,
                 businessType: Report.businessType.buttonView,
@@ -552,7 +579,7 @@ function addSliderNavDeBtn(isSmall = false) {
         let smallDom = document.querySelector('a[href="/compose/tweet"]').parentNode.parentNode;
         let deBtn3 = document.getElementById('de-btn3');
         if (smallDom && !deBtn3) {
-            smallDom.appendChild(dom.deBtn3);
+            dom && dom.deBtn3 && smallDom.appendChild(dom.deBtn3);
             Report.reportLog({
                 pageSource: Report.pageSource.mainPage,
                 businessType: Report.businessType.buttonView,
@@ -564,6 +591,7 @@ function addSliderNavDeBtn(isSmall = false) {
 
 function onWindowResize() {
     window.onresize = throttle(function () {
+        setTabGroupIframeStyle();
         try {
             if (tweetPublishStore.showPublishDialog) {
                 let dialog = document.querySelector('div[role="dialog"]');
@@ -604,7 +632,7 @@ function checkHasDeBtn() {
         let innerDeIcon = document.getElementById('de-btn1');
         if (toolBar && !innerDeIcon) {
             let dialogScheduleBtn = _getScheduleDom(false);
-            _addDeNetEditBtn(dialogScheduleBtn, dom.deBtn1);
+            dom && dom.deBtn1 && _addDeNetEditBtn(dialogScheduleBtn, dom.deBtn1);
         }
     } catch (e) {
         console.log(e)
@@ -614,7 +642,7 @@ function checkHasDeBtn() {
 /**
  * 点击发推,后端绑定推特id
  */
-function bindTwitterArtMethod() {
+export function bindTwitterArtMethod() {
     if (!bindTwitterArt.postId) {
         return
     }
@@ -696,6 +724,8 @@ function setIframeRedPacket(type = 'twitter') {
                     let item = res.has_post_Id_card_data[i];
                     if (item && item.post_Id && item.post_Id.indexOf('nft/') >= 0) {
                         parseCard.replaceNftDomRedPacket(item)
+                    } else if (item && item.post_Id && item.post_Id.indexOf('nft_group/') >= 0) {
+                        parseCard.replaceNftGroupDomRedPacket(item)
                     } else {
                         parseCard.replaceDOMRedPacket(item)
                     }
@@ -709,13 +739,15 @@ function setIframeRedPacket(type = 'twitter') {
     }
 }
 
-// 监听点击发推 按钮绑定事件
+// 监听点击发推 上报文案
 // document.addEventListener('click', (e) => {
 //     try {
+//         let inputEle = document.querySelector('div[contenteditable="true"]');
 //         if (e.target.dataset && e.target.dataset.testid && e.target.dataset.testid == 'tweetButton') {
-//             bindTwitterArtMethod()
+//             // 获取文案上报
+//             console.log(inputEle.innerText)
 //         } else if (e.target.closest('div[data-testid=tweetButton]')) {
-//             bindTwitterArtMethod()
+//             console.log(inputEle.innerText)
 //         }
 //     } catch (error) {
 //         console.error('error', error)
@@ -730,6 +762,14 @@ export function initExecuteScript(changes) {
             init()
         }
     }
+    if (changes.userInfo) {
+        let item = JSON.parse(changes.userInfo.newValue)
+        if (item) {
+            checkUserJoinGroup(() => {
+                showNFTGroupIcon()
+            })
+        }
+    }
 }
 
 const createNFTIframe = ({ url, id }, callback) => {
@@ -746,9 +786,9 @@ const createNFTIframe = ({ url, id }, callback) => {
 
 function initParseCard() {
     let timer = setInterval(() => {
-        let inTwitter = window.location.href.includes('twitter.com');
+        let inTwitter = window.location.host.includes('twitter.com');
         let inTwitterNode = document.querySelector('main');
-        let inFacebook = window.location.href.includes('facebook.com');
+        let inFacebook = window.location.host.includes('facebook.com');
         let inFacebookNode = document.querySelector('#facebook');
         if (inTwitter && inTwitterNode) {
             clearInterval(timer)
@@ -757,14 +797,19 @@ function initParseCard() {
                     onChangePageMain(inTwitterNode)
                     changeQueueNum(1)
                 }
+                twitterPinLogin()
+                showNFTGroupIcon()
                 if (queue_num <= 0) {
                     return
                 }
+                initGroupTip()
                 setIframeRedPacket()
                 checkHasDeBtn()
                 checkHasSliderDeBtn();
                 changeQueueNum(-1)
                 showNFTCard()
+                addGroupTab();
+                addJoinedGroupList();
             }, 1000)
         } else if (inFacebook && inFacebookNode) {
             clearInterval(timer)
@@ -790,30 +835,37 @@ export function init() {
     }
     inited = true
     console.log('init')
-
+    twitterPinLogin()
     getDiscordAuthCode();
     appendPopupPage();
 
-    chrome.runtime.sendMessage({ 
-        actionType: "CONTENT_SET_POPUP_CONFIG", 
+    chrome.runtime.sendMessage({
+        actionType: "CONTENT_SET_POPUP_CONFIG",
         data: {
             popup: 'popup.html'
-        } 
+        }
     }, () => { });
 
-    let where = window.location.href.indexOf('twitter.com') < 0 && window.location.href.indexOf('facebook.com') < 0;
+    let where = window.location.host.indexOf('twitter.com') < 0 && window.location.host.indexOf('facebook.com') < 0;
     if (where) {
         return
     }
-    twitterPinLogin();
+    
+    if (window.location.host.includes('twitter.com')) {
+        showNFTCard()
+        showNFTGroupIcon()
+        addEventAction();
+        checkUserJoinGroup();
+        renderDom();
+        checkTwitterTaskState();
+        initBuyNFT();
+        addJoinedGroupList();
+        getSysTheme();
+        addGroupTab();
+    }
     // 渲染dom
     initParseCard()
-    showNFTCard()
-    renderDom();
-    checkTwitterTaskState();
 
-    onBodyClick();
-    initBuyNFT();
 
     getChromeStorage("popupShowPublishDialog", (res) => {
         console.log("popupShowPublishDialog", res);
@@ -1156,6 +1208,13 @@ export function doTaskTwitterAPI({ task_data, task_type }) {
     }
 }
 
+export function showJoinDialog(data) {
+    let iframe = document.querySelector('#nftProjectId')
+    iframe.style.display = 'block'
+    iframe.contentWindow.postMessage({ actionType: 'SHOW_JOIN_DATA', data }, '*');
+    iframe.src = chrome.runtime.getURL(`/iframe/buy-nft.html#/group?params=${JSON.stringify(data)}&time=${new Date().getTime()}`)
+}
+
 const TwitterFollowAPI = (item, tweet_Id) => {
     fetch("https://twitter.com/i/api/1.1/friendships/create.json", {
         "headers": {
@@ -1248,10 +1307,172 @@ const TwitterLikeAPI = (tweet_Id) => {
     })
 }
 
+
+
+let click_old_time = new Date().getTime()
+export const showTwitterPost = (data) => {
+    let click_new_time = new Date().getTime()
+    if ((click_new_time - click_old_time) < 3000) {
+        return
+    }
+    click_old_time = click_new_time
+    showEditTweet(() => {
+        clearPostContent(() => {
+            contentGetNftPostPre({
+                groupId: data.groupId
+            })
+        })
+    })
+    // addPublishTipsIframe({ type: 'nft' })
+
+    setGroupTabStatus();
+}
+
+export function publishNFTTweetEvent({ groupId, postId, srcContent }, callback) {
+    setTimeout(() => {
+        let publishTweetBtn;
+        let dialog = document.querySelector('div[role="dialog"]');
+        if (dialog) {
+            publishTweetBtn = dialog.querySelector('div[data-testid="tweetButton"]');
+        } else {
+            let domMain = document.querySelector('main[role="main"]');
+            publishTweetBtn = domMain && domMain.querySelector('div[data-testid="tweetButton"]');
+        }
+        publishTweetBtn && publishTweetBtn.addEventListener('click', function () {
+            // 获取文案上报
+            publishNFTTweetPost({ postId, srcContent, groupId })
+            // 绑定推文id
+            bindTwitterArt.needBind = true;
+            bindTwitterArt.postId = postId;
+            callback && callback()
+
+            jumpTwitterDetailByAlert()
+            setTimeout(() => {
+                setGroupTabSelfStyle({
+                    groupColor: 'rgb(83, 100, 113)',
+                    groupFontWeight: '500',
+                    lineDisplay: 'none'
+                });
+                chrome.runtime.sendMessage({
+                    actionType: "SWITCH_GROUP_BANNER_STATUS",
+                    data: { type: 'arrow' }
+                }, () => { });
+            }, 2000);
+        });
+    }, 800)
+}
+
+export const publishNFTTweetPost = ({ postId, srcContent, groupId }) => {
+    let inputEle = document.querySelector('div[contenteditable="true"]');
+    let textContent = inputEle.innerText
+    srcContent = srcContent.replaceAll('#DNFT', '')
+    srcContent = srcContent.replaceAll('⬇️', '')
+    srcContent = srcContent.replaceAll('Join', '')
+    srcContent = srcContent.replaceAll('Now:', '')
+    let arr = srcContent.split(' ') || []
+    arr.forEach((item) => {
+        if (textContent.includes(item)) {
+            textContent = textContent.replaceAll(item, '')
+        }
+    })
+    textContent = textContent.replaceAll('#DNFT', '')
+    textContent = textContent.replaceAll('⬇️', '')
+    textContent = textContent.replaceAll('Join', '')
+    textContent = textContent.replaceAll('Now:', '')
+
+    let formData = {
+        groupId,
+        textContent
+    }
+    let params = {
+        postBizData: JSON.stringify(formData),
+        postSrc: 1, //1 twitter
+        postType: 2, //2 nft
+        postId
+    }
+    chrome.runtime.sendMessage({
+        actionType: "CONTENT_NFT_TXT_PUBLISH",
+        data: params
+    }, () => { });
+}
+
+const contentGetNftPostPre = (data) => {
+    chrome.runtime.sendMessage({
+        actionType: "CONTENT_GET_TWITTER_NFT_POST_PRE",
+        data
+    }, () => { });
+}
+
+let tweet_nft_content = {}
+export const setTwitterTextarea = (params, num = 5) => {
+    if (num <= 0) {
+        return
+    }
+    tweet_nft_content = params
+    let inputEle = document.querySelector('div[contenteditable="true"]');
+    if (inputEle) {
+        inputEle.focus();
+        document.execCommand("insertText", false, '');
+        setTimeout(() => {
+            document.execCommand("insertText", false, params.srcContent);
+            publishNFTTweetEvent(params, bindTwitterArtMethod)
+        }, 1000)
+    } else {
+        setTimeout(() => {
+            num--
+            setTwitterTextarea(params, num)
+        }, 500);
+    }
+}
+
+
+const initGroupTip = () => {
+    let arr = window.location.pathname.split('/') || []
+    if (location.pathname == '/compose/tweet') {
+        return
+    }
+    if (!document.querySelector('div[data-testid=UserName]')) {
+        return
+    }
+    if (arr.length >= 2) {
+        let twitterAccount = arr[1]
+        let iframe_banner = document.querySelector('#denet_group_banner')
+        if (iframe_banner) {
+            if (twitterAccount != getQueryStringByUrl(iframe_banner.src, 'twitterAccount')) {
+                iframe_banner.style.display = 'none'
+                // iframe_banner.src = chrome.runtime.getURL(`/iframe/group-card.html?twitterAccount=${twitterAccount}`)
+                iframe_banner.contentWindow.postMessage({ actionType: 'SHOW_BANNER', twitterAccount }, '*');
+            }
+            return
+        }
+        try {
+            let dom = document.querySelector('div[data-testid="ScrollSnap-SwipeableList"]').closest('nav')
+            let iframe = document.createElement('iframe')
+            iframe.id = 'denet_group_banner'
+            iframe.style.cssText = 'border: medium none; display:none; width:100%; height:100px;'
+            iframe.src = chrome.runtime.getURL(`/iframe/group-card.html?twitterAccount=${twitterAccount}`)
+            // iframe.contentWindow.postMessage({ actionType: 'SHOW_BANNER', twitterAccount }, '*');
+            if (dom && !dom.parentNode.children[0].querySelector('iframe')) {
+                // dom.parentNode.insertBefore(iframe, dom)
+                dom.parentNode.children[0].appendChild(iframe)
+            }
+
+        } catch (error) {
+
+        }
+    }
+}
+
+export const showGroupTip = () => {
+    let dom_denet_group_banner = document.querySelector('#denet_group_banner')
+    dom_denet_group_banner.style.display = 'block';
+    groupBtnStyleChange();
+}
+
 export const hideBuyNFT = () => {
     let iframe = document.querySelector('#nftProjectId')
     iframe.style.display = 'none'
-    iframe.src = ''
+    iframe.src =''
 }
 
 export const showBuyNFT = ({ nft_project_Id }) => {
@@ -1260,11 +1481,11 @@ export const showBuyNFT = ({ nft_project_Id }) => {
     }
     let iframe = document.querySelector('#nftProjectId')
     iframe.style.display = 'block'
-    iframe.src = chrome.runtime.getURL(`/iframe/buy-nft.html?nftProjectId=${nft_project_Id}`)
+    iframe.src = chrome.runtime.getURL(`/iframe/buy-nft.html#/?nftProjectId=${nft_project_Id}`)
 }
 
 const initBuyNFT = () => {
-    let url = chrome.runtime.getURL(`/iframe/buy-nft.html`)
+    let url = ''
     let id = `nftProjectId`
     createNFTIframe({ url, id })
 }
@@ -1300,13 +1521,28 @@ export const showNFTSale = () => {
     document.querySelector('div[id="de-nft-node"]').style.display = 'block';
 }
 
+export const addEventAction = () => {
+    let urlInfo = new URL(window.location.href)
+    let isTwitter = urlInfo.hostname === 'twitter.com'
+
+    // 页面滚动
+    if (isTwitter) {
+        // 首页
+        if (urlInfo.pathname === '/home') {
+            window.addEventListener('scroll', () => {
+                hideNFTGroupList()
+            })
+        }
+    }
+}
+
 export const appendPopupPage = (params = {}) => {
     let { path = '' } = params;
 
     let iframe = document.createElement('iframe');
-        iframe.id = 'de-popup-page';
-        iframe.src = chrome.runtime.getURL('/iframe/popup-page.html') + `#${path}`;
-        iframe.style.cssText = `border: medium none; width: 375px;
+    iframe.id = 'de-popup-page';
+    iframe.src = chrome.runtime.getURL('/iframe/popup-page.html') + `#${path}`;
+    iframe.style.cssText = `border: medium none; width: 375px;
         height: 650px;position: fixed; right: 16px; top: 16px;background: #FFFFFF;border: 0.5px solid #919191;box-shadow: 0px 4px 20px rgba(0, 0, 0, 0.2);box-sizing: border-box;z-index: 90000;
         animation-duration: 0.5s !important;
         animation-timing-function: ease-in-out !important;
@@ -1315,8 +1551,8 @@ export const appendPopupPage = (params = {}) => {
     let iframeContent = document.getElementById('de-popup-page');
 
     let overlayDom = document.createElement('div');
-        overlayDom.id = 'de-popup-overlay';
-        overlayDom.style.cssText = `position: fixed;z-index: 88888;top: 0;
+    overlayDom.id = 'de-popup-overlay';
+    overlayDom.style.cssText = `position: fixed;z-index: 88888;top: 0;
         left: 0;width: 100%;height: 100%;opacity: 0;display: none`;
 
     let overlay = document.getElementById('de-popup-overlay');
@@ -1324,14 +1560,14 @@ export const appendPopupPage = (params = {}) => {
 
     if (!iframeContent && body) {
         document.querySelector('body').appendChild(iframe);
-        if(!overlay) {
+        if (!overlay) {
             document.querySelector('body').appendChild(overlayDom);
 
-            overlayDom.addEventListener('click', function() {
+            overlayDom.addEventListener('click', function () {
                 hidePopupPage();
             })
         } else {
-            overlay.addEventListener('click', function() {
+            overlay.addEventListener('click', function () {
                 hidePopupPage();
             })
         }
@@ -1340,7 +1576,7 @@ export const appendPopupPage = (params = {}) => {
 
 let showPopupPageFrom = '';
 export const showPopupPage = (params = {}) => {
-    let { path = '', from } = params;
+    let { path = '', from, showJoinGroupFinish = false } = params;
     showPopupPageFrom = from;
     hidePinTips();
     hideNoticeBindTweet();
@@ -1350,29 +1586,32 @@ export const showPopupPage = (params = {}) => {
         appendPopupPage();
         iframe = document.getElementById('de-popup-page');
     }
-    if(iframe) {
-        if(path) {
+    if (iframe) {
+        if (path) {
             iframe.src = chrome.runtime.getURL('/iframe/popup-page.html') + `#${path}`;
         }
         iframe.style.transform = 'translateX(-' + 395 + 'px)';
-    
-        chrome.runtime.sendMessage({ 
-            actionType: "CONTENT_POPUP_PAGE_SHOW", 
-            data: { } 
+
+        chrome.runtime.sendMessage({
+            actionType: "CONTENT_POPUP_PAGE_SHOW",
+            data: {
+                path,
+                showJoinGroupFinish,
+            }
         }, () => { });
 
-        chrome.runtime.sendMessage({ 
-            actionType: "CONTENT_SET_POPUP_CONFIG", 
+        chrome.runtime.sendMessage({
+            actionType: "CONTENT_SET_POPUP_CONFIG",
             data: {
                 popup: ''
-            } 
+            }
         }, () => { });
-    
+
         let overlay = document.getElementById('de-popup-overlay');
         overlay.style.display = 'block';
-    
+
         let htmlDom = document.querySelector('html');
-        if(htmlDom) {
+        if (htmlDom) {
             htmlDom.style.overflowY = 'hidden';
         }
     }
@@ -1387,21 +1626,21 @@ export const hidePopupPage = () => {
         overlay.style.display = 'none';
 
         let htmlDom = document.querySelector('html');
-        if(htmlDom) {
+        if (htmlDom) {
             htmlDom.style.overflowY = 'auto';
         }
 
-        chrome.runtime.sendMessage({ 
-            actionType: "CONTENT_SET_POPUP_CONFIG", 
+        chrome.runtime.sendMessage({
+            actionType: "CONTENT_SET_POPUP_CONFIG",
             data: {
                 popup: 'popup.html'
-            } 
+            }
         }, () => { });
 
-        if(showPopupPageFrom == 'BUY_NFT_FINISH') {
-            chrome.runtime.sendMessage({ 
-                actionType: "CONTENT_GET_PINED", 
-                data: {} 
+        if (showPopupPageFrom == 'BUY_NFT_FINISH') {
+            chrome.runtime.sendMessage({
+                actionType: "CONTENT_GET_PINED",
+                data: {}
             }, () => { });
             showPopupPageFrom = '';
         }
@@ -1413,8 +1652,8 @@ export const tiggerInjectPopupPage = () => {
     if (iframeContent) {
         hidePinTips();
         hideNoticeBindTweet();
-        let {transform = ''} = iframeContent.style;
-        if(transform == 'translateX(385px)' || !transform) {
+        let { transform = '' } = iframeContent.style;
+        if (transform == 'translateX(385px)' || !transform) {
             showPopupPage();
         } else {
             hidePopupPage();
@@ -1423,8 +1662,8 @@ export const tiggerInjectPopupPage = () => {
         appendPopupPage();
         setTimeout(() => {
             let iframe = document.getElementById('de-popup-page');
-            let {transform = ''} = iframe.style;
-            if(transform == 'translateX(385px)' || !transform) {
+            let { transform = '' } = iframe.style;
+            if (transform == 'translateX(385px)' || !transform) {
                 showPopupPage();
             } else {
                 hidePopupPage();
@@ -1434,41 +1673,617 @@ export const tiggerInjectPopupPage = () => {
 }
 
 const onBodyClick = () => {
-    if(window.location.href.indexOf('api.twitter.com') < 0) {
+    if (window.location.href.indexOf('api.twitter.com') < 0) {
         document.querySelector('body').addEventListener('click', function () {
             console.log('click')
             // hidePopupPage();
         })
-    } 
+    }
 }
 
-
 export const setPopupConfByPopupPage = () => {
     let iframe = document.getElementById('de-popup-page');
     if (iframe) {
-        let {transform = ''} = iframe.style;
+        let { transform = '' } = iframe.style;
 
-        if(transform && transform == 'translateX(-395px)') {
-            chrome.runtime.sendMessage({ 
-                actionType: "CONTENT_SET_POPUP_CONFIG", 
+        if (transform && transform == 'translateX(-395px)') {
+            chrome.runtime.sendMessage({
+                actionType: "CONTENT_SET_POPUP_CONFIG",
                 data: {
                     popup: ''
-                } 
+                }
             }, () => { });
         } else {
-            chrome.runtime.sendMessage({ 
-                actionType: "CONTENT_SET_POPUP_CONFIG", 
+            chrome.runtime.sendMessage({
+                actionType: "CONTENT_SET_POPUP_CONFIG",
                 data: {
                     popup: 'popup.html'
-                } 
+                }
             }, () => { });
         }
     } else {
-        chrome.runtime.sendMessage({ 
-            actionType: "CONTENT_SET_POPUP_CONFIG", 
+        chrome.runtime.sendMessage({
+            actionType: "CONTENT_SET_POPUP_CONFIG",
             data: {
                 popup: 'popup.html'
-            } 
+            }
         }, () => { });
     }
-}
+}
+
+
+/** 
+ * 
+ * Group Tab List Start 
+ */
+
+/**
+ * 
+ * 创建 Group Tab 
+ */
+const createGroupTabNode = () => {
+    let groupIcon = document.createElement('img');
+    groupIcon.id = 'de-group-tab-icon'
+    groupIcon.src = require("@/assets/img/icon-group-tab-item.png");
+    groupIcon.style.cssText = 'width:20px;height: 20px;margin-right:4px;';
+
+    let divNode = document.createElement('div');
+    divNode.style.cssText = 'display: flex; align-items: center;height: 100%';
+    divNode.appendChild(groupIcon);
+    divNode.appendChild(document.createTextNode('Group'));
+
+
+    let lineDom = document.createElement('div');
+    lineDom.id = 'de-tab-line';
+    lineDom.style.cssText = `border-radius: 9999px;
+        position: absolute;
+        bottom: 0px;
+        min-width: 56px;
+        align-self: center;
+        height: 4px;
+        background-color: rgb(29, 155, 240);
+        display: none`;
+
+    let groupTab = document.createElement('div');
+    groupTab.id = 'de-nav-tab-group';
+    groupTab.style.cssText = `z-index: 1;
+        position: relative;
+        display: flex;
+        min-width: 56px;
+        -webkit-box-pack: center;
+        justify-content: center;
+        -webkit-box-align: center;
+        align-items: center;
+        text-align: center;
+        padding: 0px 16px;
+        color: rgb(83, 100, 113);
+        font-weight: 700;
+        height: 53px;
+        cursor: pointer;
+        font: 500 15px / 20px TwitterChirp, -apple-system, "system-ui", "Segoe UI", Roboto, Helvetica, Arial, sans-serif;`;
+
+    groupTab.appendChild(divNode);
+    groupTab.appendChild(lineDom);
+
+    return groupTab;
+}
+
+const groupBtnStyleChange = () => {
+    let tab = getGroupTabNode();
+    if (tab) {
+        let line = tab.querySelector('#de-tab-line');
+        if (line) {
+            let { display } = line.style;
+            if (display != 'none') {
+                chrome.runtime.sendMessage({
+                    actionType: "SWITCH_GROUP_BANNER_STATUS",
+                    data: { type: 'btn' }
+                }, () => { });
+            }
+        }
+    }
+}
+
+const getGroupTabNode = () => {
+    let tab = document.querySelector('#de-nav-tab-group');
+    return tab;
+}
+
+const addGroupTab = () => {
+    if (!document.querySelector('div[data-testid=UserName]')) {
+        return
+    }
+    let tabListDom = document.querySelector('div[role="tablist"]');
+    let groupItemTab = getGroupTabNode();
+
+    if (tabListDom && !groupItemTab) {
+        let groupTab = createGroupTabNode();
+
+        tabListDom.appendChild(groupTab);
+
+        groupTab.addEventListener('mouseenter', function () {
+            groupTab.style.background = 'rgba(15, 20, 25, 0.1)'
+        });
+        groupTab.addEventListener('mouseleave', function () {
+            groupTab.style.background = 'none'
+        });
+
+        setTimeout(() => {
+            let count = 0;
+            hiddenMaskWeb3Tab(count);
+        }, 1300)
+
+        addGroupTabEventListener();
+    }
+
+    removeTweetTabEvent({
+        tabListDom
+    });
+
+    addTweetTabEventListener({
+        tabListDom
+    });
+
+    addTabGroupContent(() => {
+        checkNeedSelectGroupTab();
+    });
+}
+
+const hiddenMaskWeb3Tab = (count) => {
+    setTimeout(() => {
+        count++;
+        if (count < 6) {
+            let tab = getMaskWeb3Tab();
+            if (tab) {
+                tab.style.display = 'none'
+            } else {
+                hiddenMaskWeb3Tab(count);
+            }
+        }
+    }, 1000);
+}
+
+/**
+ * 跳转到个人主页 检查是否需要选中 Group tab
+ */
+const checkNeedSelectGroupTab = () => {
+    if (window.location.pathname != '/home') {
+        setTimeout(() => {
+            getChromeStorage('groupTabData', (res) => {
+                console.log('groupTabData', res);
+                if (res && res.deTabVal == 'deGroupTab') {
+                    chrome.storage.local.remove("groupTabData");
+                    setTimeout(() => {
+                        selectGroupTab();
+                    }, 300)
+                }
+            })
+        }, 1300)
+    }
+}
+
+/** 选中 Group tab */
+export const selectGroupTab = () => {
+    let groupTab = getGroupTabNode();
+    if (groupTab) {
+        groupTab.click();
+    }
+};
+
+export const groupTipsSelectGroupTab = (params = {}) => {
+    if(params.type =='btn') {
+        let groupTab = getGroupTabNode();
+        if (groupTab) {
+            let line = groupTab.querySelector('#de-tab-line');
+            if (line) {
+                let { display } = line.style;
+                if (display == 'none') {
+                    groupTab.click();
+                }
+            }
+        }
+    } else {
+        selectGroupTab();
+    }
+}
+/**
+ * 
+ * Group tab点击事件监听
+ */
+const addGroupTabEventListener = () => {
+    let groupTab = getGroupTabNode();
+    groupTab.addEventListener('click', function () {
+        let groupColor = systemInfo.theme == 'light' ? 'rgb(15, 20, 25)' : '#fff';
+        setGroupTabSelfStyle({
+            groupColor: groupColor,
+            groupFontWeight: '700',
+            lineDisplay: 'block'
+        });
+
+        setTweetActiveTabStyle({
+            color: 'rgb(83, 100, 113)',
+            display: 'none'
+        });
+
+        setTabContentStyle({
+            tweetTabContentDisply: 'none',
+            iframeContentDisplay: 'block'
+        });
+
+        refreshTabGroup();
+
+        window.addEventListener('scroll', addPageScrollEvent);
+
+        let tipsDom = document.querySelector('#denet_group_banner');
+        if (tipsDom) {
+            chrome.runtime.sendMessage({
+                actionType: "SWITCH_GROUP_BANNER_STATUS",
+                data: { type: 'btn' }
+            }, () => { });
+        } else {
+            onShowGroupBanner();
+        }
+    })
+}
+
+const onShowGroupBanner = () => {
+    chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
+        switch (req.actionType) {
+            case 'IFRAME_SHOW_GROUP_TIP':
+                chrome.runtime.sendMessage({
+                    actionType: "SWITCH_GROUP_BANNER_STATUS",
+                    data: { type: 'btn' }
+                }, () => { });
+                if (!getGroupTabNode()) {
+                    addGroupTab();
+                }
+                break
+        }
+    })
+}
+
+const addPageScrollEvent = () => {
+    let wrapperDom = document.querySelector('html');
+    let contentDom = document.querySelector('main[role="main"]');
+    let data = {
+        wrapperHeight: wrapperDom.offsetHeight,
+        wrapperScrollTop: wrapperDom.scrollTop,
+        contentHeight: contentDom.offsetHeight
+    }
+
+    chrome.runtime.sendMessage({
+        actionType: "CONTENT_GROUP_LIST_SCROLL",
+        data: data
+    }, () => { });
+};
+
+const removeTweetTabEvent = (params) => {
+    let { tabListDom } = params;
+
+    if (tabListDom) {
+        let tweetTabItem = tabListDom.querySelectorAll('div[role="presentation"]');
+        if (tweetTabItem.length) {
+            for (let i = 0; i < tweetTabItem.length; i++) {
+                let item = tweetTabItem[i];
+                item.removeEventListener('click', TweetTabEventHandler)
+            }
+        }
+    }
+}
+
+/**
+ * 
+ * twitter tab点击事件监听
+ */
+const addTweetTabEventListener = (params) => {
+    let { tabListDom } = params;
+    let groupItemTab = getGroupTabNode();
+
+    if (tabListDom && groupItemTab) {
+        // 监听twitter tab点击事件
+        let tweetTabItem = tabListDom.querySelectorAll('div[role="presentation"]');
+        if (tweetTabItem.length) {
+            for (let i = 0; i < tweetTabItem.length; i++) {
+                let item = tweetTabItem[i];
+                item.addEventListener('click', TweetTabEventHandler)
+            }
+        }
+    }
+}
+
+const TweetTabEventHandler = () => {
+    window.removeEventListener('scroll', addPageScrollEvent);
+
+    setGroupTabSelfStyle({
+        groupColor: 'rgb(83, 100, 113)',
+        groupFontWeight: '500',
+        lineDisplay: 'none'
+    });
+
+    setTabContentStyle({
+        tweetTabContentDisply: 'block',
+        iframeContentDisplay: 'none'
+    });
+
+    setTweetActiveTabStyle({
+        color: 'rgb(15, 20, 25)',
+        display: 'block'
+    });
+
+    chrome.runtime.sendMessage({
+        actionType: "SWITCH_GROUP_BANNER_STATUS",
+        data: { type: 'arrow' }
+    }, () => { });
+};
+
+
+/**
+ * 设置 Group Tab 样式 
+ * */
+const setGroupTabSelfStyle = (params = {}) => {
+    let { groupColor, groupFontWeight, lineDisplay } = params;
+    let groupTab = getGroupTabNode();
+    if (groupTab) {
+        groupTab.style.color = groupColor;
+        groupTab.style.fontWeight = groupFontWeight;
+
+        let lineDom = groupTab.querySelector('#de-tab-line');
+        if (lineDom) {
+            lineDom.style.display = lineDisplay;
+        }
+    }
+};
+
+/**
+ * 切换到 Group tab时 刷新列表
+ */
+export const refreshTabGroup = () => {
+    chrome.runtime.sendMessage({
+        actionType: "CONTENT_REFRESH_TAB_GROUP_LIST",
+        data: {}
+    }, () => { });
+}
+
+/**
+ * 
+ * tab选中时设置 激活 的字体样式和选中条
+ */
+const setTweetActiveTabStyle = (params) => {
+    let { color, display } = params || {};
+    let tab = document.querySelector('a[aria-selected="true"]');
+    if (tab) {
+        let tweetActiveTab = tab.querySelector('div');
+        if (tweetActiveTab) {
+            tweetActiveTab.style.color = color;
+
+            let tweetTabLine = tweetActiveTab.querySelector('div');
+            if (tweetTabLine) {
+                tweetTabLine.style.display = display;
+            }
+        }
+    }
+}
+
+/**
+ * 
+ * 设置 tab 切换时 tab内容的样式(显示隐藏)
+ */
+const setTabContentStyle = (params) => {
+    let { tweetTabContentDisply, iframeContentDisplay } = params;
+    let tweetTabContent = getTweetTabContent();
+    if (tweetTabContent) {
+        if (tweetTabContentDisply == 'block') {
+            let { visibility } = tweetTabContent.style;
+            if (visibility == 'hidden') {
+                tweetTabContent.style.visibility = 'visible';
+                tweetTabContent.style.height = 'auto';
+                tweetTabContent.style.overflow = 'auto';
+            }
+        } else {
+            tweetTabContent.style.visibility = 'hidden';
+            tweetTabContent.style.height = '0px';
+            tweetTabContent.style.overflow = 'hidden';
+            tweetTabContent.style.margin = '0';
+        }
+    }
+
+    let iframeContent = getGroupTabContentNode();
+
+    if (!iframeContent) {
+        addTabGroupContent();
+    }
+    setTimeout(() => {
+        iframeContent = getGroupTabContentNode();
+        if (iframeContent) {
+            iframeContent.style.display = iframeContentDisplay;
+        }
+    })
+};
+
+/**
+ * 
+ * 获取 twitter tab 下的内容
+ */
+const getTweetTabContent = () => {
+    let tweetTabContent = document.querySelector('[data-testid="primaryColumn"] [role="navigation"] + * > div[aria-label]:not([role="progressbar"])') || document.querySelector('div[data-testid="emptyState"]');
+    return tweetTabContent;
+}
+
+/**
+ * 注入 Group List 内容
+ */
+const addTabGroupContent = (cb) => {
+    let params = {
+        windowLocation: window.location
+    }
+    let iframe = document.createElement('iframe');
+    iframe.id = 'de-tab-group-content';
+    iframe.src = chrome.runtime.getURL('/iframe/tab-group.html') + `?params=${JSON.stringify(params)}`;
+    iframe.style.cssText = `border: medium none; height: 500px;display: none`
+
+    let iframeContent = getGroupTabContentNode();
+
+    let tweetTabContent = getTweetTabContent();
+    if (!iframeContent) {
+        if (tweetTabContent && tweetTabContent.parentElement) {
+            tweetTabContent.parentElement.appendChild(iframe);
+            cb && cb();
+        }
+    }
+};
+
+const getGroupTabContentNode = () => {
+    let content = document.getElementById('de-tab-group-content');
+    return content;
+}
+
+const setGroupTabStatus = () => {
+    let groupTab = getGroupTabNode();
+    if (groupTab) {
+        let line = groupTab.querySelector('#de-tab-line');
+        if (line) {
+            let { display } = line.style;
+            if (display != 'none') {
+                groupTab.click();
+            }
+        }
+    } else {
+        setTimeout(() => {
+            addGroupTab();
+        }, 2000)
+    }
+
+    clearInterval(fixProfileTabAutoTimer);
+    fixProfileTabAutoSwitch();
+};
+
+const fixProfileTabAutoSwitch = () => {
+    fixProfileTabAutoTimer = setInterval(() => {
+        let groupTab = getGroupTabNode();
+        let tweetTab = document.querySelector('a[aria-selected="true"]');
+
+        if (groupTab) {
+            let line = groupTab.querySelector('#de-tab-line');
+            if (line) {
+                let { display } = line.style;
+                if (display != 'none' && tweetTab) {
+                    let groupContent = getGroupTabContentNode();
+                    if (groupContent) {
+                        let tweetTabContent = getTweetTabContent();
+                        let { visibility } = tweetTabContent.style;
+                        let { display } = groupContent.style;
+
+                        if (display == 'block' && visibility != 'hidden') {
+                            groupTab.click();
+                        }
+                    }
+                }
+            }
+        }
+    }, 1000)
+}
+
+/**
+ * 
+ * 设置Tab Group Iframe 样式
+ */
+export const setTabGroupIframeStyle = (params) => {
+    let iframeContent = getGroupTabContentNode();
+    if (iframeContent) {
+        let htmlHeight =  document.querySelector('html').offsetHeight;
+        let primaryColumnHeightn = document.querySelector('div[data-testid="primaryColumn"]').offsetHeight;
+        let height = primaryColumnHeightn > htmlHeight ? primaryColumnHeightn : htmlHeight;
+        iframeContent.style.height = height + 'px';
+    }
+}
+
+/**
+ * mask web3 Tab
+ * 
+ */
+const getMaskWeb3Tab = () => {
+    let tab = document.querySelector('div[data-testid="ScrollSnap-nextButtonWrapper"] + span');
+    return tab;
+}
+
+export const pageJumpHandler = (params) => {
+    let { url } = params
+    if (url) {
+        window.open(url)
+    }
+}
+
+export const getTweetProfileNavTop = (params) => {
+    let top = document.querySelector('div[role="tablist"]').closest('nav').getBoundingClientRect().top;
+
+    chrome.runtime.sendMessage({
+        actionType: "CONTENT_SEND_GROUP_NAV_TOP", data: {
+            top,
+            scrollTop: params.scrollTop
+        }
+    }, () => { })
+}
+
+
+export const setGroupInfo = (params = {}) => {
+    tweetAccountBindGroupInfo.groupInfo = params;
+    let groupTab = getGroupTabNode();
+
+    if (!params.nftGroupId) {
+        let { pathname = '' } = window.location;
+        if (pathname == "/compose/tweet") {
+            return;
+        };
+        if (groupTab) {
+            groupTab.style.display = 'none';
+        } else {
+            setTimeout(() => {
+                groupTab = getGroupTabNode();
+                if (groupTab) {
+                    groupTab.style.display = 'none';
+                }
+            }, 800)
+        }
+    } else {
+        if (groupTab) {
+            let { display } = groupTab.style;
+            if (display == 'none') {
+                groupTab.style.display = 'block';
+            }
+        }
+    }
+}
+
+const getSysTheme = () => {
+    const themeMedia = window.matchMedia("(prefers-color-scheme: light)");
+    if (themeMedia.matches) {
+        systemInfo.theme = 'light'
+    } else {
+        systemInfo.theme = 'dark'
+    }
+    themeMedia.addListener(e => {
+        addGroupTab()
+        if (e.matches) {
+            systemInfo.theme = 'light'
+        } else {
+            systemInfo.theme = 'dark'
+        }
+    });
+}
+
+
+/** 
+ * 
+ * Group Tab List End 
+ * 
+ */
+
+
+export const loginSuccessHandle = () => {
+    // 检查是否漏出group图标
+    checkUserJoinGroup(() => {
+        showNFTGroupIcon()
+        addEventAction()
+        addJoinedGroupList();
+    })
+}

+ 8 - 2
src/manifest.json

@@ -2,7 +2,7 @@
     "manifest_version": 3,
     "name": "DeNet",
     "description": "Growing more twitter followers with Denet",
-    "version": "1.1.0",
+    "version": "1.1.1",
     "background": {
         "service_worker": "/js/background.js"
     },
@@ -68,8 +68,14 @@
                 "/iframe/publish-tips.html",
                 "/iframe/bind-tweet.html",
                 "/iframe/nft-card.html",
+                "/iframe/nft-group.html",
+                "/iframe/nft-group-card.html",
                 "/iframe/buy-nft.html",
-                "/iframe/popup-page.html"
+                "/iframe/group-card.html",
+                "/iframe/popup-page.html",
+                "/iframe/popup-page.html",
+                "/iframe/tab-group.html",
+                "/iframe/joined-group-list.html"
             ],
             "matches": [
                 "<all_urls>"

+ 8 - 0
src/router/buy-nft.js

@@ -1,14 +1,22 @@
 import { createRouter, createWebHashHistory, createWebHistory } from "vue-router"
+import GroupTip from '@/view/iframe/buy-nft/group/tip.vue'
 import Home from '@/view/iframe/buy-nft/buy/home.vue'
 import Pay from '@/view/iframe/buy-nft/buy/pay.vue'
 import OpenBox from '@/view/iframe/buy-nft/buy/open-box.vue'
 
 let routes = [
+    
+
     {
         path: '/',
         name: 'Home',
         component: Home,
     },
+    {
+        path: '/group',
+        name: 'GroupTip',
+        component: GroupTip,
+    },
     {
         path: "/pay",
         name: 'Pay',

+ 27 - 2
src/uilts/chromeExtension.js

@@ -101,8 +101,33 @@ export function concatChromeCookie({ name, url }, value_obj) {
 }
 
 export function removeChromeCookie(params, cb) {
-    let {name, url} = params;
-    chrome.cookies.remove({name, url}, () => {
+    let { name, url } = params;
+    chrome.cookies.remove({ name, url }, () => {
         cb && cb
     })
+}
+
+export function sendChromeTabMessage(params) {
+    chrome.tabs.getCurrent((tab) => {
+        chrome.tabs.sendMessage(tab.id, params, (res) => { console.log(res) });
+    })
+}
+
+export function checkIsLogin(callback) {
+    return new Promise((resolve) => {
+        getChromeStorage('userInfo', (_userInfo) => {
+            if (!_userInfo) {
+                chrome.runtime.sendMessage(
+                    { actionType: "POPUP_LOGIN", data: "" },
+                    (response) => {
+                        console.log("res", response);
+                    }
+                )
+                callback && callback()
+                resolve(_userInfo)
+            } else {
+                resolve(_userInfo)
+            }
+        })
+    })
 }

+ 67 - 43
src/uilts/help.js

@@ -7,6 +7,15 @@ export function getQueryString(name) {
   return null
 }
 
+export function getQueryStringByUrl(url = '', name = '') {
+  let reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i")
+  let r = url.split('?')[1].match(reg)
+  if (r != null) {
+    return window.decodeURIComponent(r[2])
+  }
+  return null
+}
+
 export function debounce(fn, delay) {
   let timer; // 定时器
   return function (...args) { // 形成闭包
@@ -63,69 +72,84 @@ export function guid() {
 }
 
 export function scaleNumber(num) {
-    let length = num.toString().split('.')[1].length;
-    
-    let scale = '1';
-    
-    for(let i = 0; i < length; i++) {
-        scale += '0';
-    }
+  let length = num.toString().split('.')[1].length;
+
+  let scale = '1';
+
+  for (let i = 0; i < length; i++) {
+    scale += '0';
+  }
 
-    let val = num * scale;
-    return {
-      val,
-      scale
-    };
+  let val = num * scale;
+  return {
+    val,
+    scale
+  };
 }
 
-export function getBit (value) {
+export function getBit(value) {
   const reg = /([0-9]+\.[0-9]{4})[0-9]*/;
-  if(value) {
+  if (value) {
     let str = value.toString();
-    str = str.replace(reg,"$1");
+    str = str.replace(reg, "$1");
     return str;
   } else {
     return value;
   }
 }
 
-export function getCookie(name){
+export function getCookie(name) {
   var strcookie = document.cookie;//获取cookie字符串
   var arrcookie = strcookie.split("; ");//分割
   //遍历匹配
-  for ( var i = 0; i < arrcookie.length; i++) {
-      var arr = arrcookie[i].split("=");
-      if (arr[0] == name){
-          return arr[1];
-      }
+  for (var i = 0; i < arrcookie.length; i++) {
+    var arr = arrcookie[i].split("=");
+    if (arr[0] == name) {
+      return arr[1];
+    }
   }
   return "";
 }
 
 export function nextTick(fn, time = 50) {
-    return new Promise((resolve, reject) => {
-        setTimeout(() => {
-            if (fn) fn();
-            resolve();
-        }, time)
-    })
+  return new Promise((resolve, reject) => {
+    setTimeout(() => {
+      if (fn) fn();
+      resolve();
+    }, time)
+  })
 }
 
 export function getBrowser() {
-    let browser;
-    let UserAgent = navigator.userAgent.toLowerCase();
-    if (UserAgent.indexOf('chrome') > -1 && UserAgent.indexOf('safari') > -1) {
-        browser = `Chrome`
-    } else if (UserAgent.indexOf('firefox') > -1) {
-        browser = `Firefox`
-    } else if (UserAgent.indexOf('opera') > -1) {
-        browser = `Opera`
-    } else if (UserAgent.indexOf('safari') > -1 && UserAgent.indexOf('chrome') == -1) {
-        browser = `Safari`
-    } else if (UserAgent.indexOf('edge') > -1) {
-        browser = `Edge`
-    } else {
-        browser = `Other`
-    }
-    return browser;
+  let browser;
+  let UserAgent = navigator.userAgent.toLowerCase();
+  if (UserAgent.indexOf('chrome') > -1 && UserAgent.indexOf('safari') > -1) {
+    browser = `Chrome`
+  } else if (UserAgent.indexOf('firefox') > -1) {
+    browser = `Firefox`
+  } else if (UserAgent.indexOf('opera') > -1) {
+    browser = `Opera`
+  } else if (UserAgent.indexOf('safari') > -1 && UserAgent.indexOf('chrome') == -1) {
+    browser = `Safari`
+  } else if (UserAgent.indexOf('edge') > -1) {
+    browser = `Edge`
+  } else {
+    browser = `Other`
+  }
+  return browser;
+}
+
+export function getOffsetRect(element) {
+  var oTop = element.offsetTop;
+  var oLeft = element.offsetLeft;
+  var current = element.offsetParent;
+  while (current !== null) {
+    oTop += current.offsetTop;
+    oLeft += current.offsetLeft;
+    current = current.offsetParent;
+  }
+  return {
+    top: oTop,
+    left: oLeft,
+  }
 }

+ 112 - 0
src/view/components/join-group-finish-dialog.vue

@@ -0,0 +1,112 @@
+<template>
+    <div class="join-group-overlay" :style="{'position': position}" v-if="dialogVisible">
+        <div class="content-wrapper" :style="contentStyle">
+            <img :src="require('@/assets/svg/icon-celebration.svg')" 
+                class="icon-celebration"
+                :style="iconStyle">
+            <div class="desc" :style="descStyle">{{content}}</div>
+            <div class="btn-wrapper">
+                <div class="btn confirm" @click="confirm">Finish</div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { ref, defineEmits, defineProps } from "vue";
+const props = defineProps({
+    dialogVisible: {
+        type: Boolean,
+        default: false,
+    },
+    content: {
+        type: String,
+        default: 'Joined Successfully'
+    },
+    position: {
+        type: String,
+        default: 'fixed'
+    },
+    contentStyle: {
+        type: Object,
+        default: () => {
+            return {}
+        }
+    },
+    iconStyle: {
+        type: Object,
+        default: () => {
+            return {}
+        }
+    },
+    descStyle: {
+        type: Object,
+        default: () => {
+            return {}
+        }
+    }
+});
+
+const emits = defineEmits(["confirm"]);
+
+const confirm = () => {
+    emits("confirm", {});
+};
+
+</script>
+
+<style lang="scss" scoped>
+.join-group-overlay {
+    position: fixed;
+    top: 0;
+    right: 0;
+    bottom: 0;
+    left: 0;
+    z-index: 3000;
+    height: 100%;
+    background-color: rgba(0, 0, 0, 0.5);
+    overflow: auto;
+
+    .content-wrapper {
+        position: absolute;
+        left: 50%;
+        top: 50%;
+        background: #FFFFFF;
+        border-radius: 20px;
+        box-sizing: border-box;
+        transform: translate(-50%, -50%);
+        text-align: center;
+        width: 500px;
+
+        .icon-celebration {
+            width: 120px;
+            margin-top: 60px;
+        }
+
+        .desc {
+            font-weight: 600;
+            font-size: 22px;
+            margin-top: 36px;
+            margin-bottom: 58px;
+        }
+
+        .btn-wrapper {
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            .confirm {
+                width: 100%;
+                padding: 14px;
+                box-sizing: border-box;
+                background: #1D9BF0;
+                color: #fff;
+                border-radius: 100px;
+                font-weight: 600;
+                font-size: 16px;
+                cursor: pointer;
+                margin: 0px 20px 20px 20px;
+            }
+        }
+    }
+}
+</style>

+ 137 - 0
src/view/components/nft-group-list.vue

@@ -0,0 +1,137 @@
+<template>
+    <div class="list" v-show="detail && detail.length">
+        <div class="list-content">
+            <div
+                class="item"
+                :key="index"
+                v-for="(item, index) in detail"
+                @click="clickHandler(item)">
+                <div class="logo">
+                    <img :src="item.nftGroupIcon" />
+                    <div class="badge" v-if="showBadge && item.newPostCount > 0">
+                        {{item.newPostCount}}
+                    </div>
+                </div>
+                <div class="text">{{item.nftGroupName}}</div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { onBeforeMount, ref, defineEmits, defineProps, watch } from 'vue'
+import { listJoinNftGroup } from '@/http/nft'
+
+const props = defineProps({
+    showBadge: {
+        type: Boolean,
+        default: false,
+    }
+})
+
+let pageNum = 1;
+let pageSize = 1000;
+let detail = ref(null);
+let emits = defineEmits(['clickCallBack', 'updateList']);
+
+watch(detail, (newVal) => {
+    emits('updateList', newVal);
+})
+
+const clickHandler = (item) => {
+    emits('clickCallBack', item);
+}
+
+const getDetail = () => {
+    listJoinNftGroup({
+        params: {
+            pageNum,
+            pageSize
+        }
+    }).then(res => {
+        let { data } = res;
+        if (data !== null) {
+            detail.value = data;
+        } else {
+            emits('updateList', data);
+        }
+    })
+}
+
+const onPageVisbile = () => {
+    document.addEventListener('visibilitychange', function () {
+        let isHidden = document.hidden;
+        if (!isHidden) {
+            getDetail();
+        }
+    });
+}
+
+onBeforeMount(() => {
+    getDetail();
+    onPageVisbile();
+})
+</script>
+
+<style lang='scss'>
+body {
+    margin: 0;
+    padding: 0;
+    user-select: none;
+}
+
+.list {
+    overflow-y: auto;
+    .item {
+        display: flex;
+        height: 48px;
+        margin: 2px 0;
+        cursor: pointer;
+        flex-direction: row;
+        align-items: center;
+        .logo {
+            width: 38px;
+            height: 38px;
+            border: 1px solid #E3E3E3;
+            border-radius: 6px;
+            background: #FFFFFF;
+            margin-right: 16px;
+            position: relative;
+
+            img {
+                width: 100%;
+                height: 100%;
+                border-radius: 6px;
+            }
+
+            .badge {
+                min-width: 16px;
+                min-height: 16px;
+                padding: 0 4px;
+                font-weight: 600;
+                font-size: 12px;
+                color: #fff;
+                background: #1D9BF0;
+                border: 1px solid #F7F9F9;
+                border-radius: 100px;
+                box-sizing: border-box;
+                position: absolute;
+                top: -8px;
+                left: 29px;
+                min-width: 18px;
+                min-height: 18px;
+                display: flex;
+                align-items: center;
+                justify-content: center;
+            }
+        }
+        .text {
+            display: flex;
+            flex: 1;
+            font-size: 16px;
+            font-weight: 500;
+            line-height: 19px;
+        }
+    }
+}
+</style>

+ 0 - 2
src/view/iframe/bind-tweet/bind-tweet.vue

@@ -15,9 +15,7 @@
 </template>
 
 <script setup>
-/* eslint-disable */
 import { onMounted, ref } from "vue";
-import { ElMessage } from 'element-plus'
 import { getChromeStorage } from '@/uilts/chromeExtension.js'
 import { getQueryString } from '@/uilts/help.js'
 import { terminatedLuckdrop } from "@/http/redPacket";

+ 7 - 2
src/view/iframe/buy-nft/buy/home.vue

@@ -80,12 +80,13 @@
     </div>
 </template>
 <script setup>
-import router from "@/router/buy-nft.js";
+import { useRouter } from 'vue-router'
 import { onMounted, reactive, inject, } from "vue";
 import { getNftMysteryBoxSaleInfo } from "@/http/nft";
 import BtnLoading from '../components/btn-loading.vue'
 import { getQueryString } from "@/uilts/help";
 let pay_info = inject('pay_info');
+const router = useRouter()
 let state = reactive({
     data: {
         salePlans: [
@@ -118,7 +119,11 @@ const clickJump = (item) => {
     router.push({ path: '/pay' });
 }
 onMounted(() => {
-    let nft_project_Id = getQueryString('nftProjectId') || ''
+    let nft_project_Id = router.currentRoute.value.query.nftProjectId
+    let nft_group_Id = router.currentRoute.value.query.nft_group_Id
+    if(nft_group_Id){
+        pay_info.nft_group_Id = nft_group_Id
+    }
     if (!nft_project_Id) {
         return
     }

+ 7 - 2
src/view/iframe/buy-nft/buy/open-box.vue

@@ -20,7 +20,8 @@ let state = reactive({
     },
     nft: {
         data: []
-    }
+    },
+    showJoinGroupFinish: false
 })
 
 const setAllNoShow = () => {
@@ -51,7 +52,8 @@ const showNFTs = () => {
                 chrome.tabs.sendMessage(tab.id, {
                     actionType: "IFRAME_TWITTER_SHOW_POPUP_PAGE",
                     data: {
-                        from: 'BUY_NFT_FINISH'
+                        from: 'BUY_NFT_FINISH',
+                        showJoinGroupFinish: state.showJoinGroupFinish
                     }
                 }, (res) => { });
                 router.replace('/')
@@ -63,6 +65,9 @@ const showNFTs = () => {
 
 onMounted(() => {
     state.nft.data = pay_info.buy_items || []
+    if (pay_info.nft_group_Id) {
+        state.showJoinGroupFinish = true
+    }
     setTimeout(() => {
         state.box.show = false
         showNFTs()

+ 9 - 3
src/view/iframe/buy-nft/buy/pay.vue

@@ -94,6 +94,9 @@ import BtnLoading from '../components/btn-loading.vue'
 import { payNftMysteryBoxWithBalance } from "@/http/pay";
 import { getChromeStorage } from "@/uilts/chromeExtension"
 import { ElMessage } from 'element-plus'
+import "element-plus/es/components/message/style/css";
+import { sendChromeTabMessage } from '@/uilts/chromeExtension.js';
+
 let pay_info = inject('pay_info');
 let state = reactive({
     loading: {
@@ -122,12 +125,14 @@ const clickPlay = () => {
     payNftMysteryBoxWithBalance({
         params: {
             nftProjectId: pay_info.home.nftProjectId,
-            salePlanId: pay_info.home.sale_plan.salePlanId
+            salePlanId: pay_info.home.sale_plan.salePlanId,
+            nftGroupId: pay_info.nft_group_Id
         }
     }).then((res) => {
         state.loading.show = false
         if (res.code == 0) {
             pay_info.buy_items = res.data.buyItems
+            sendChromeTabMessage({ actionType: "FINISH_GROUP_BANNNER" }, () => { })
             router.push({ path: '/open_box' });
         } else {
             let msg = ''
@@ -256,9 +261,10 @@ onMounted(() => {
                 margin-top: 15px;
                 display: flex;
                 justify-content: space-between;
-                div{
+
+                div {
                     display: flex;
-                        align-items: center;
+                    align-items: center;
                 }
 
                 img {

+ 247 - 0
src/view/iframe/buy-nft/group/tip.vue

@@ -0,0 +1,247 @@
+<template>
+    <div class="dialog">
+        <div class="area-title">
+            <img :src="require('@/assets/svg/icon-close.svg')" @click="clickClose" />
+            <div class="title">Join Group</div>
+        </div>
+        <div class="area-content" v-show="state.show == 'tip'">
+            <!--  -->
+            <div class="logo">
+                <img :src="state.params.nftGroupIcon" />
+            </div>
+
+            <div class="title">
+                {{ state.params.nftGroupName }}
+            </div>
+            <div class="tip">
+                Join to Get Posting Permissions and Badges
+            </div>
+            <div class="tip2">
+
+                <img :src="require('@/assets/svg/icon-line.svg')" />
+                <span>Own this Group's NFT to Join</span>
+                <img :src="require('@/assets/svg/icon-line.svg')" />
+            </div>
+            <div class="btn" v-if="state.params.type == 'buy'" @click="clickBuy">
+                Buy NFT to Join
+            </div>
+            <div class="btn" v-if="state.params.type == 'join'" @click="clickJoin">
+                Join Now
+            </div>
+        </div>
+        <div class="area-content success" v-show="state.show == 'success'">
+            <!--  -->
+            <div class="logo">
+                <img :src="require('@/assets/svg/icon-group-success.svg')" />
+            </div>
+
+            <div class="title">
+                Joined Successfully
+            </div>
+
+            <div class="btn" @click="clickFinish">
+                Finish
+            </div>
+        </div>
+    </div>
+
+</template>
+
+<script setup>
+import { reactive, onMounted } from 'vue'
+import { useRouter } from 'vue-router'
+import { setGroupJoin } from '@/http/group.js'
+import { ElMessage } from 'element-plus'
+import { sendChromeTabMessage } from '@/uilts/chromeExtension.js';
+let state = reactive({
+    show: 'tip',
+    params: {}
+})
+const router = useRouter()
+
+
+const clickJoin = () => {
+    // 请求接口
+    setGroupJoin({
+        params: {
+            nftGroupId: state.params.nft_group_Id
+        }
+    }).then((res) => {
+        if (res.code == 0) {
+            state.show = 'success'
+            state.params.type = 'buy'
+            sendChromeTabMessage({ actionType: "FINISH_GROUP_BANNNER" }, () => { })
+        } else {
+            let msg = ''
+            switch (String(res.code)) {
+                case '5201':
+                    msg = 'need to buy NFT to join the discussion group'
+                    break
+                default:
+                    msg = res.msg
+            }
+            ElMessage({
+                message: msg,
+                grouping: true,
+                type: 'warning',
+                appendTo: document.body
+            })
+        }
+    })
+}
+
+const clickBuy = () => {
+    // 传输参数
+    router.push(`/?nft_group_Id=${state.params.nft_group_Id}&nftProjectId=${state.params.buyNftProjectId}`)
+}
+const clickClose = () => {
+    chrome.tabs.getCurrent((tab) => {
+        chrome.tabs.sendMessage(tab.id, {
+            actionType: "IFRAME_TWITTER_HIDE_BUY_NFT",
+        }, (res) => { });
+    })
+}
+
+window.addEventListener("message", function (event) {
+    if (event.data) {
+        switch (event.data.actionType) {
+            case 'SHOW_JOIN_DATA':
+                state.params = event.data.data
+        }
+    }
+});
+
+const clickFinish = () => {
+    clickClose()
+}
+
+onMounted(() => {
+    let params = router.currentRoute.value.query.params || {}
+    state.params = JSON.parse(params)
+})
+
+</script>
+<style lang="scss" scoped>
+.dialog {
+    background: #fff;
+    border-radius: 25px;
+    width: 500px;
+    height: 420px;
+    z-index: 23;
+    display: flex;
+    flex-direction: column;
+
+
+    .area-title {
+        width: 100%;
+        min-height: 48px;
+        display: flex;
+        align-items: center;
+        border-bottom: 1px solid #D9D9D9;
+        font-weight: 500;
+        font-size: 16px;
+        letter-spacing: 0.3px;
+        color: #000000;
+
+        img {
+            width: 24p;
+            height: 24px;
+            margin-left: 14px;
+            margin-right: 12px;
+            cursor: pointer;
+        }
+
+    }
+
+    .area-content {
+        flex: 1;
+        display: flex;
+        justify-content: flex-start;
+        flex-direction: column;
+
+        align-items: center;
+
+        .logo {
+            width: 80px;
+            height: 80px;
+            border: 1px solid #D0BAA6;
+            border-radius: 5px;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            margin-top: 33px;
+            margin-bottom: 25px;
+
+            img {
+                width: 75px;
+                height: 75px;
+
+            }
+        }
+
+        .title {
+            color: #000000;
+            font-size: 18px;
+
+            font-weight: 600;
+            margin-bottom: 13px;
+
+        }
+
+        .tip {
+            color: #9E9E9E;
+            font-weight: 400;
+            font-size: 16px;
+            margin-bottom: 66px;
+        }
+
+        .tip2 {
+            margin-bottom: 28px;
+            display: flex;
+            align-items: center;
+
+            span {
+                font-weight: 500;
+                font-size: 14px;
+                margin: 0 10px;
+            }
+
+            img {}
+        }
+
+        .btn {
+            width: 470px;
+            height: 46px;
+            background: #1D9BF0;
+            border-radius: 100px;
+            text-align: center;
+            line-height: 46px;
+            font-weight: 600;
+            font-size: 16px;
+            color: #fff;
+            cursor: pointer;
+        }
+    }
+
+    .success {
+        .logo {
+            width: 120px;
+            height: 120px;
+            margin-bottom: 36px;
+            margin-top: 61px;
+            border: 0;
+
+            img {
+                width: 100%;
+                height: 100%;
+            }
+        }
+
+        .title {
+            font-weight: 600;
+            font-size: 22px;
+            margin-bottom: 60px;
+        }
+    }
+}
+</style>

+ 238 - 0
src/view/iframe/group-card/card.vue

@@ -0,0 +1,238 @@
+<template>
+    <div id="denet_group_tip" v-if="state.data" style="display: flex;z-index: 100; background-color: #356789;">
+        <div style="flex:1; display: flex; align-items: center;">
+            <div>
+                <img :src="state.data.nftGroupIcon" style="width:54px;height:54px; margin: 0 20px;" alt="" />
+            </div>
+            <div>
+                <p style="font-weight: 700; font-size: 18px; color: #fff; margin: 0; padding: 0; margin-bottom: 11px;">
+                    {{ state.data.nftGroupName }}
+                </p>
+                <div style="display: flex; ">
+                    <div style="display: flex; align-items: center;">
+                        <img :src="require('@/assets/svg/icon-user.svg')"
+                            style="width:16px;height:16px; margin-right: 5px;" alt="" />
+                        <span style="color: #fff;">{{ state.data.memberCount }} Member</span>
+                    </div>
+                    <div style="display: flex; align-items: center; margin-left: 17px;">
+                        <img :src="require('@/assets/svg/icon-messgae.svg')"
+                            style="width:16px;height:16px; margin-right: 5px;" alt="" />
+                        <span style="color: #fff;">{{ state.data.postCount }} Posts</span>
+
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div
+            style="width: 180px;height: 100%; display: flex; align-items: center; justify-content: center; position: relative;">
+            <div v-show="state.show == 'post'" style="width: 140px; height: 40px; display:flex; align-items: center; justify-content: center; background: #1D9BF0; border-radius: 50px;
+                cursor: pointer;
+                " @click="clickPost">
+                <img :src="require('@/assets/svg/icon-messgae.svg')" style="width:16px;height:16px;" alt="">
+                <span style="margin-left: 7px; font-size: 15px;font-weight: 700; color: #fff; ">Post</span>
+            </div>
+            <div v-show="state.show == 'join'"
+                style="width: 140px; height: 40px; display:flex; align-items: center; justify-content: center; background: #1D9BF0; border-radius: 50px; cursor: pointer;"
+                @click="clickJoin">
+                <span style="margin-left: 7px; font-size: 15px;font-weight: 700; color: #fff; ">Join Now</span>
+            </div>
+
+            <svg @click="clickArrow" v-show="state.show == 'arrow'" id="denet_tip_group_arrow"
+                style="position: absolute; right: 20px; cursor: pointer;" width="40" height="40" viewBox="0 0 40 40"
+                fill="none" xmlns="http://www.w3.org/2000/svg">
+                <path d="M15 10L26 19.6875L15 29.375" stroke="white" stroke-width="2" />
+            </svg>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { reactive, onBeforeMount } from 'vue'
+import { getTwitterNftGroupInfo } from "@/http/group";
+import { getQueryString } from '@/uilts/help.js';
+import { sendChromeTabMessage, checkIsLogin } from '@/uilts/chromeExtension.js';
+
+let state = reactive({
+    show: 'arrow', //join
+    show2: 'join',
+    data: {},
+    twitterAccount: ''
+})
+
+// 显示加入小组弹框
+async function clickJoin() {
+    sendChromeTabMessage({ actionType: "SWITCH_GROUP_STATUS" }, () => { })
+    let _userInfo = await checkIsLogin()
+    if (!_userInfo) {
+        return
+    }
+    // 如果没购买过 弹出购买
+    if (state.data.buyNftStatus == 0) {
+        sendChromeTabMessage({
+            actionType: "IFRAME_SHOW_JOIN_DIALOG",
+            data: {
+                type: 'buy',
+                buy_nft_status: state.data.buyNftStatus,
+                nft_group_Id: state.data.nftGroupId,
+                buyNftProjectId: state.data.buyNftProjectId,
+                nftGroupIcon: state.data.nftGroupIcon,
+                nftGroupName: state.data.nftGroupName
+            }
+        })
+
+        // 如果购买过 没加入 显示加入按钮
+    } else if (state.data.buyNftStatus == 1 && state.data.joinStatus == 0) {
+        sendChromeTabMessage({
+            actionType: "IFRAME_SHOW_JOIN_DIALOG",
+            data: {
+                type: 'join',
+                buy_nft_status: state.data.buyNftStatus,
+                nft_group_Id: state.data.nftGroupId,
+                nftGroupIcon: state.data.nftGroupIcon,
+                nftGroupName: state.data.nftGroupName
+            }
+        })
+    } else if (state.data.buyNftStatus == null || state.data.joinStatus == null) {
+        init()
+    }
+}
+
+const init = (callback) => {
+    getTwitterNftGroupInfo({
+        params: {
+            twitterAccount: state.twitterAccount
+        }
+    }).then((res) => {
+        if (res.code == 0) {
+            state.data = res.data
+            if (state.data) {
+                res.data.joinStatus == 0
+                // 未加入
+                if (res.data.joinStatus == 0) {
+                    state.show2 = 'join'
+                    // 已加入
+                } else if (res.data.joinStatus == 1) {
+                    state.show2 = 'post'
+                }
+                if (state.show != 'arrow') {
+                    state.show = state.show2
+                }
+                // state.show2 = 'join'
+                callback && callback()
+            }
+            sendMessageToContent({
+                actionType: 'IFRAME_GROUP_BANNER_GROUP_INFO',
+                data: res.data || {}
+            })
+        }
+    })
+}
+
+chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
+    sendResponse('')
+    switch (req.actionType) {
+        case 'FINISH_GROUP_BANNNER':
+            init()
+            break
+        case 'SWITCH_GROUP_BANNER_STATUS':
+            if (req.data.type == 'arrow') {
+                state.show = 'arrow'
+            } else {
+                state.show = state.show2
+                // console.log(11)
+                // sendChromeTabMessage({ actionType: "SWITCH_GROUP_STATUS", data: { type: 'btn' } }, () => { })
+            }
+            break
+    }
+})
+
+chrome.storage.onChanged.addListener(changes => {
+    if (changes.userInfo) {
+        let item = JSON.parse(changes.userInfo.newValue)
+        if (item) {
+            init()
+        }
+    }
+})
+
+window.addEventListener("message", function (event) {
+    if (event.data) {
+        switch (event.data.actionType) {
+            case 'SHOW_BANNER':
+                state.twitterAccount = event.data.twitterAccount
+                init()
+        }
+    }
+});
+
+
+const sendMessageToContent = (params) => {
+    let { actionType, data } = params || {};
+    chrome.tabs.getCurrent((tab) => {
+        chrome.tabs.sendMessage(tab.id, {
+            actionType,
+            data,
+        }, (res) => { console.log(res) });
+    })
+}
+const clickArrow = () => {
+    sendChromeTabMessage({ actionType: "SWITCH_GROUP_STATUS" }, () => { })
+}
+
+async function clickPost() {
+    sendChromeTabMessage({ actionType: "SWITCH_GROUP_STATUS" }, () => { })
+    let _userInfo = await checkIsLogin()
+    if (!_userInfo) {
+        return
+    }
+    // 没有购买过
+    if (state.data.buyNftStatus == 0) {
+        sendChromeTabMessage({
+            actionType: "IFRAME_SHOW_JOIN_DIALOG",
+            data: {
+                type: 'buy',
+                buy_nft_status: state.data.buyNftStatus,
+                nft_group_Id: state.data.nftGroupId,
+                buyNftProjectId: state.data.buyNftProjectId,
+                nftGroupIcon: state.data.nftGroupIcon,
+                nftGroupName: state.data.nftGroupName
+            }
+        })
+        // 购买过 && 加入过
+    } else if (state.data.buyNftStatus == 1 && state.data.joinStatus == 1) {
+        sendChromeTabMessage({
+            actionType: "IFRAME_SHOW_POST_DIALOG",
+            data: {
+                groupId: state.data.nftGroupId
+            }
+        })
+    } else if (state.data.buyNftStatus == null || state.data.joinStatus == null) {
+        init()
+    }
+}
+
+
+onBeforeMount(() => {
+    state.twitterAccount = getQueryString('twitterAccount') || ''
+    init(() => {
+        sendChromeTabMessage({ actionType: "IFRAME_SHOW_GROUP_TIP" })
+    })
+
+})
+</script>
+
+<style lang='scss'>
+html,
+body,
+#app {
+    margin: 0;
+    padding: 0;
+    width: 100%;
+    height: 100px;
+}
+
+#denet_group_tip {
+    width: 100%;
+    height: 100%;
+}
+</style>

+ 200 - 0
src/view/iframe/nft/group-card.vue

@@ -0,0 +1,200 @@
+<template>
+    <div class="card">
+        <div class="padding" v-if="detail">
+            <div class="info">
+                <div class="logo">
+                    <img :src="detail.groupIcon" />
+                </div>
+                <div class="mess">
+                    <div class="title">
+                        {{ detail.groupName }}
+                    </div>
+                    <div class="opt">
+                        <label>
+                            <img src="../../../assets/svg/icon-nft-member.svg" />
+                            <span>{{ detail.memberCount }} Member</span>
+                        </label>
+                        <label>
+                            <img src="../../../assets/svg/icon-nft-post.svg" />
+                            <span>{{ detail.postCount }} Posts</span>
+                        </label>
+                    </div>
+                </div>
+            </div>
+            <div class="join" @click="jumpUserPage">
+                {{ detail.joinStatus === 0 ? 'Join Now' : 'View' }}
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { onBeforeMount, onMounted, ref } from 'vue';
+import { getPostDetail } from '@/http/redPacket';
+import { getQueryString } from '@/uilts/help.js'
+import { srcPublishSuccess } from '@/http/publishApi'
+import { getChromeStorage, setChromeStorage } from '@/uilts/chromeExtension.js'
+
+let postId = getQueryString('projectId');
+let tweet_Id = getQueryString('tweet_Id')
+let detail = ref(null);
+
+const jumpUserPage = () => {
+    getChromeStorage('userInfo', (_userInfo) => {
+        if (!_userInfo) {
+            chrome.runtime.sendMessage(
+                { actionType: "POPUP_LOGIN", data: "" },
+                (response) => {
+                    console.log("res", response);
+                }
+            )
+        } else {
+            setChromeStorage({ groupTabData: JSON.stringify({
+                deTabVal: 'deGroupTab'
+            })})
+            window.open(`https://twitter.com/${detail.value.defaultTwitterAccount}`)
+        }
+    })
+}
+// 重新绑定
+const reSetBindTwtterId = (_params = {}) => {
+    getChromeStorage('userInfo', (_userInfo = {}) => {
+        if (_userInfo && _userInfo.uid == _params.uid) {
+            srcPublishSuccess({
+                params: {
+                    postId: postId,
+                    srcContentId: tweet_Id
+                }
+            }).then((res) => {
+                if (res.code == 0 || res.code == 3003) {
+
+                }
+            })
+        }
+    })
+}
+
+const getDetail = () => {
+    getPostDetail({
+        params: {
+            postId: postId
+        }
+    }).then(res => {
+        let { data } = res
+        if (data !== null) {
+            detail.value = JSON.parse(data.postBizData)
+        }
+    })
+}
+
+onMounted(() => {
+    getPostDetail({
+        params: {
+            postId: postId
+        }
+    }).then(res => {
+        let { data } = res
+        if (data !== null) {
+            detail.value = JSON.parse(data.postBizData)
+            if (!data.srcContentId) {
+                reSetBindTwtterId(data)
+                return
+            }
+        }
+    })
+
+    // 登录回调
+    chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
+        switch (req.actionType) {
+            case 'BG_LOGIN_SET_USERINFO_CB':
+                getDetail();
+                break;
+        }
+    })
+})
+</script>
+
+<style lang='scss'>
+html,
+body {
+    margin: 0;
+    padding: 0;
+    user-select: none;
+}
+
+.card {
+    height: 180px;
+    border-radius: 12px;
+    background: url('../../../assets/svg/icon-nft-group-pc.svg') no-repeat right bottom #48B1F7;
+
+    .padding {
+        padding: 20px;
+
+        .info {
+            display: flex;
+            flex-direction: row;
+            align-items: center;
+            height: 100px;
+
+            .logo {
+                display: flex;
+                align-items: center;
+                justify-content: center;
+                width: 54px;
+                height: 54px;
+                border-radius: 6px;
+                background: #FFFFFF;
+                margin-right: 20px;
+
+                img {
+                    width: 50px;
+                    height: 50px;
+                    border-radius: 6px;
+                }
+            }
+
+            .mess {
+                flex: 1;
+
+                .title {
+                    overflow: hidden;
+                    color: #FFFFFF;
+                    font-size: 18px;
+                    line-height: 21px;
+                    text-overflow: ellipsis;
+                    white-space: nowrap;
+                }
+
+                .opt {
+                    color: #FFFFFF;
+                    font-size: 12px;
+                    margin-top: 8px;
+
+                    label {
+                        margin-right: 18px;
+
+                        img {
+                            margin-top: -4px;
+                            margin-right: 4px;
+                            vertical-align: middle;
+                        }
+                    }
+                }
+            }
+        }
+
+        .join {
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            height: 40px;
+            cursor: pointer;
+            color: #FFFFFF;
+            font-size: 16px;
+            font-weight: bold;
+            background: #101419;
+            border-radius: 100px;
+        }
+    }
+}
+</style>

+ 27 - 0
src/view/iframe/nft/group.vue

@@ -0,0 +1,27 @@
+<template>
+    <nft-group-list @clickCallBack="clickHandler"></nft-group-list>
+</template>
+
+<script setup>
+import NftGroupList from '@/view/components/nft-group-list.vue';
+import { prePost } from '@/http/nft'
+
+const clickHandler = (item) => {
+    prePost({
+        params: {
+            groupId: item.nftGroupId
+        }
+    }).then(res => {
+        let { code, data = {} } = res
+        if (code === 0) {
+            chrome.tabs.getCurrent((tab) => {
+                chrome.tabs.sendMessage(tab.id, { actionType: "IFRAME_NFT_GROUP_LIST_HIDE" });
+                chrome.tabs.sendMessage(tab.id, {
+                    actionType: "IFRAME_NFT_GROUP_SET_CONTENT",
+                    publishRes: data
+                });
+            })
+        }
+    })
+}
+</script>

+ 20 - 5
src/view/iframe/publish-tips/publish-tips.vue

@@ -2,7 +2,7 @@
     <div class="publish-tips-wrapper">
         <div class="top">
             <img src="@/assets/svg/icon-bells.svg" class="icon-bells">
-            <div class="text-wrapper">
+            <div class="text-wrapper" v-show="type == 'Giveaway'">
                 <div>
                     Do not delete the
                 </div>
@@ -11,10 +11,17 @@
                     otherwise the giveaway will not be available
                 </div>
             </div>
+            <div class="text-wrapper" v-show="type == 'NFT'">
+                <div>
+                    Do not delete the
+                </div>
+                <div>
+                    <span>#DNFT </span>and<span> NFT Group Link</span>
+                    otherwise the NFT Post will not be aviilble
+                </div>
+            </div>
         </div>
-        <div class="copy-btn" 
-            :data-clipboard-text="strContent" 
-            @click="copyToken">Copy giveaways link</div>
+        <div class="copy-btn" :data-clipboard-text="strContent" @click="copyToken">Copy giveaways link</div>
     </div>
 </template>
 
@@ -23,10 +30,12 @@
 import { onMounted, ref } from "vue";
 import { message } from 'ant-design-vue';
 import { getChromeStorage } from '@/uilts/chromeExtension.js'
+import { getQueryString } from '@/uilts/help.js';
 
 let ClipboardJS = require('clipboard');
 
 let strContent = ref('');
+let type = ref('Giveaway')
 
 const copyToken = () => {
     var clipboard = new ClipboardJS('.copy-btn');
@@ -53,7 +62,11 @@ const setSrcContent = async () => {
 
 onMounted(() => {
     setSrcContent();
-}) 
+    let type = getQueryString('type') || ''
+    if (type == 'nft') {
+        type.value = 'nft'
+    }
+})
 
 </script>
 
@@ -61,6 +74,7 @@ onMounted(() => {
 body {
     overflow-y: hidden;
 }
+
 .publish-tips-wrapper {
     width: 100%;
     box-sizing: border-box;
@@ -68,6 +82,7 @@ body {
     border-radius: 12px;
     padding: 15px;
     margin-top: 88px;
+
     .top {
         display: flex;
 

+ 6 - 3
src/view/iframe/publish/give-dialog.vue

@@ -241,9 +241,9 @@
                                         </div>
                                         
                                         <div>
-                                            <el-switch
+                                            <a-switch
                                                 v-if="item.type > 3"
-                                                v-model="item.checked"
+                                                v-model:checked="item.checked"
                                                 @change="formSwitchChange($event, item, index)"
                                             />
                                             <!-- <img src="@/assets/svg/icon-task-close.svg" 
@@ -452,8 +452,9 @@ import { getFrontConfig } from "@/http/account";
 import {setChromeStorage, getChromeStorage} from "@/uilts/chromeExtension"
 import { debounce, getBit } from "@/uilts/help"
 import Report from "@/log-center/log"
-import { ElMessage, ElLoading, ElDropdown, ElDropdownMenu, ElDropdownItem } from "element-plus";
+import { ElMessage, ElLoading } from "element-plus";
 import "element-plus/es/components/message/style/css";
+import "element-plus/es/components/loading/style/css";
 
 import {create, all} from "mathjs";
 
@@ -1948,6 +1949,8 @@ onMounted(() => {
 
                         .currency-select-wrapper {
                             padding: 0 !important;
+                            overflow: hidden;
+                            
                             input {
                                 padding-right: 14px;
                             }

+ 1 - 1
src/view/iframe/red-packet/red-packet.vue

@@ -1206,7 +1206,7 @@ const doTaskReport = (req, sender) => {
 onMounted(() => {
   state.process_mode = process.env.NODE_ENV
   state.postId = getQueryString('postId')
-  state.window_origin = getQueryString('window_origin');
+  state.window_origin = getQueryString('window_origin') || '';
   if (state.window_origin.indexOf('twitter.com') > -1) {
     state.tweetId = getQueryString('tweetId')
     state.tweet_author = getQueryString('tweet_author');

+ 125 - 0
src/view/iframe/tab-group/joined-group-list.vue

@@ -0,0 +1,125 @@
+<template>
+    <div class="group-list-page" ref="pageWrapperDom">
+        <div class="title">
+            <img class="icon" :src="require('@/assets/svg/icon-joined-group-logo.svg')" >
+            NFT Owners Group
+        </div>
+        <div class="content-wrapper">
+            <nft-group-list style="height: 100%" 
+                ref="groupListDom"
+                :showBadge="true" 
+                @clickCallBack="clickHandler"
+                @updateList="updateList"></nft-group-list>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { onMounted, ref, nextTick } from "vue";
+import NftGroupList from '@/view/components/nft-group-list.vue';
+
+import { sendChromeTabMessage, setChromeStorage } from '@/uilts/chromeExtension.js';
+
+let groupListDom = ref(null);
+
+const clickHandler = (params) => {
+    setChromeStorage({ groupTabData: JSON.stringify({
+        deTabVal: 'deGroupTab'
+    })})
+    let url = `https://twitter.com/${params.defaultTwitterAccount}`;
+    sendMessageToContent({
+        actionType: 'IFRAME_PAGE_JUMP',
+        data: {
+            url
+        }
+    })
+}
+
+const sendMessageToContent = (params) => {
+    let {actionType, data} = params || {};
+    chrome.tabs.getCurrent((tab) => {
+        chrome.tabs.sendMessage(tab.id, {
+        actionType,
+        data,
+        }, (res) => { console.log(res) });
+    })
+}
+
+const updateList = (data) => {
+    setHeight(data);
+};
+
+const setHeight = (data) => {
+    const maxHeight = 321;
+    nextTick(() => {
+        if(!data || !data.length) { 
+            sendChromeTabMessage({
+                actionType: "IFRAME_JOINED_GROUP_SET_STYLE",
+                data: {
+                    height: '0px'
+                }
+            })
+            return;
+        }
+        let listDom = document.querySelector('.list-content');
+        if(listDom) {
+            const titleDomHeight = 56, marginBottom = 12;
+            let height = maxHeight;
+            let contentHeight = listDom.offsetHeight + titleDomHeight + marginBottom;
+
+            if(contentHeight < maxHeight) {
+                height =  contentHeight;
+            } else {
+                height = maxHeight;
+            }
+            sendChromeTabMessage({
+                actionType: "IFRAME_JOINED_GROUP_SET_STYLE",
+                data: {
+                    height: height + 'px'
+                }
+            })
+        }
+    })
+}
+
+onMounted(() => {
+}) 
+</script>
+
+<style  lang="scss">
+html, body, #app {
+    width: 100%;
+    height: 100%;
+    margin: 0;
+    padding: 0;
+}
+
+.group-list-page {
+    height: 100%;
+    overflow-y: auto;
+    background: #F7F9F9;
+
+    padding: 0 16px;
+    box-sizing: border-box;
+
+    .title {
+        font-weight: 800;
+        font-size: 18px;
+        box-sizing: border-box;
+        height: 56px;
+        display: flex;
+        align-items: center;
+
+        .icon {
+            margin-right: 8px;
+        }
+
+    }
+    .content-wrapper {
+        height: calc(100% - 60px);
+        overflow-y: auto;
+        padding-left: 4px;
+        box-sizing: border-box;
+    }
+}
+</style>

+ 424 - 0
src/view/iframe/tab-group/tab-group.vue

@@ -0,0 +1,424 @@
+<template>
+    <div class="tab-group-page" ref="pageWrapperDom" @scroll="pageScroll">
+        <div class="list-wrapper" ref="listWrapperDom">
+            
+            <template v-if="listData.length">
+                <div class="list-item" 
+                    v-for="(item, index) in listData" 
+                    :key="index"
+                    @click="clickItem(item, index)">
+                    <div class="left">
+                        <img :src="item.avatarUrl" class="icon-avatar">
+                    </div>
+                    <div class="right">
+                        <div class="top">
+                            <div class="icon-nft-wrapper">
+                                <el-popover
+                                    :width="340"
+                                    placement="right"
+                                    trigger="hover"
+                                    popper-style="background: #FFFFFF;
+                                        box-shadow: 0px 4px 20px rgba(0, 0, 0, 0.2);
+                                        border-radius: 20px;
+                                        padding: 20px;
+                                        box-sizing: border-box;">
+                                    <template #reference>
+                                        <img v-if="item.nftItem" :src="item.nftItem.imagePath" class="icon-nft" @click.stop="">
+                                    </template>
+                                    <template #default>
+                                        <div class="preview-nft" v-if="item.nftItem">
+                                            <img :src="item.nftItem.imagePath" class="icon-nft-big">
+                                            <div class="content">
+                                                <div class="nft-name">
+                                                    {{item.nftItem.nftItemName}} 
+                                                </div>
+                                                <div class="nft-desc">
+                                                    <div v-if="item.nftItem.metadata" v-html="item.nftItem.metadata.description"></div>
+                                                </div>
+                                            </div>
+                                        </div>
+                                    </template>
+                                </el-popover>
+                            </div>
+                            <div class="nick-name">
+                                {{item.nickName}}
+                            </div>
+                            <div class="screen-name">
+                                {{item.screenName}}
+                            </div>
+                        </div>
+                        <div class="post-content" v-html="item.textContent"></div>
+                    </div>
+                </div>
+            </template>
+            <template v-if="loading && !listData.length" >
+                    <img :src="require('@/assets/svg/icon-tweet-loading.svg')" 
+                            class="icon-loading" >
+            </template>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { onMounted, ref } from "vue";
+import { getGroupPostList, getTwitterNftGroupInfo } from '@/http/nft'
+import { getQueryString } from '@/uilts/help.js'
+import { ElPopover } from "element-plus";
+
+
+let twitterAccount = '';
+let groupInfo = {};
+
+let listData = ref([])
+let listWrapperDom = ref(null);
+let pageWrapperDom = ref(null);
+let loading = ref(false);
+
+let listReqParams = {
+    params: {
+        pageSize: 100,
+        preTimestamp: ''
+    },
+    loadMore: false,
+    isInit: false,
+};
+
+const clickItem = (data, index) => {
+    if(data.srcContentId) {
+        let url = `https://twitter.com/${data.screenName}/status/${data.srcContentId}`;
+        sendMessageToContent({
+            actionType: 'IFRAME_PAGE_JUMP',
+            data: {
+                url
+            }
+        })
+    }
+}
+
+function onRuntimeMsg() {
+    chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
+        switch (req.actionType) {
+            case 'CONTENT_REFRESH_TAB_GROUP_LIST':
+                listReqParams.params.preTimestamp = ''
+                initData();
+                break;
+            case 'CONTENT_GROUP_LIST_SCROLL':
+                nextPage(req.data);
+                break;
+            case 'CONTENT_SEND_GROUP_NAV_TOP':
+                styleHandler(req.data);
+                break;
+        }
+    })
+}
+
+const nextPage = (params) => {
+    let {wrapperHeight, wrapperScrollTop, contentHeight} = params;
+    if (wrapperHeight + wrapperScrollTop >= (contentHeight - 50)) {
+        console.log('next---');
+        if(pageWrapperDom.value && pageWrapperDom.value.style.overflowY != 'auto') {
+            pageWrapperDom.value.style.overflowY = 'auto'
+        }
+    }
+};
+
+const pageScroll = (e) => {
+    sendMessageToContent({
+        actionType: "IFREME_TAB_GROUP_CONTENT_GET_NAV_TOP",
+        data: {
+            scrollTop: e.target.scrollTop
+        }
+    })
+}
+
+const styleHandler = (data) => {
+    if(data.top > 53) {
+        if(pageWrapperDom.value && pageWrapperDom.value.style.overflowY != 'hidden') {
+            pageWrapperDom.value.style.overflowY = 'hidden'
+        }
+    } else {
+        if(pageWrapperDom.value && pageWrapperDom.value.style.overflowY != 'auto') {
+            pageWrapperDom.value.style.overflowY = 'auto'
+        }
+        innerPageNext(data);
+    }
+}
+
+const innerPageNext = (data) => {
+    let wrapperHeight = pageWrapperDom.value.offsetHeight;
+    let listContentHeight = listWrapperDom.value.offsetHeight;
+    let scrollTop = data.scrollTop || 0;
+    if (
+        listReqParams.loadMore === false &&
+        wrapperHeight + scrollTop >= (listContentHeight - 100)
+    ) {
+        listReqParams.loadMore = true;
+        let dataLength = listData.value.length;
+        if(dataLength) {
+            listReqParams.params.preTimestamp = listData.value[dataLength - 1]['createTimestamp'];
+        }
+        getListData();
+    }
+}
+
+const sendMessageToContent = (params) => {
+    let {actionType, data} = params || {};
+    chrome.tabs.getCurrent((tab) => {
+        chrome.tabs.sendMessage(tab.id, {
+        actionType,
+        data,
+        }, (res) => { console.log(res) });
+    })
+}
+
+const getListData = () => {
+    getGroupPostList({
+        params: {
+            pageSize: listReqParams.params.pageSize,
+            preTimestamp: listReqParams.params.preTimestamp,
+            groupId: groupInfo.nftGroupId
+        }
+    }).then(res => {
+        loading.value = false;
+        if (res.code == 0) {
+            let resData = res.data;
+            if (resData.length) {
+                for (let i = 0; i < resData.length; i++) {
+                    let nftItem = resData[i]["nftItem"];
+                    if(nftItem) {
+                        let matedata = nftItem['metadata'];
+                        if(matedata) {
+                            resData[i]["nftItem"]['metadata'] = JSON.parse(matedata);
+                        }
+                    }
+                }
+
+                if (!listReqParams.params.preTimestamp) {
+                    listData.value = resData;
+                } else {
+                    let data = dataList.value;
+                    data = data.concat(resData);
+                    listData.value = data;
+                }
+                listReqParams.loadMore = false;
+            }
+        }
+    })
+}
+
+const initData = () => {
+    let {windowLocation} = JSON.parse(getQueryString('params'));
+    if(windowLocation.pathname) {
+        let arr =  windowLocation.pathname.split('/');
+        if(arr.length >= 2){
+            twitterAccount = arr[1];
+            if(twitterAccount) {
+                getTwitterNftGroupInfo({
+                    params: {
+                        twitterAccount
+                    }
+                }).then(res => {
+                    if(res.code == 0) {
+                        groupInfo = res.data || {};
+                        if(!groupInfo.nftGroupId) return;
+                        loading.value = true;
+                        getListData()
+                    }
+                })
+            }
+        }
+    }
+}
+
+onMounted(() => {
+    onRuntimeMsg();
+    initData();
+
+    sendMessageToContent({
+        actionType: "IFREME_TAB_GROUP_SET_IFRAME_HEIGHT",
+        data: {
+            height: listWrapperDom.value.offsetHeight + 10
+        }
+    })
+}) 
+</script>
+
+<style  lang="scss">
+html, body, #app {
+    width: 100%;
+    height: 100%;
+    margin: 0;
+    padding: 0;
+}
+
+.el-popper__arrow {
+    display: none !important;
+}
+
+@media (prefers-color-scheme: light) {
+    body {
+        background: #fff;
+    }
+}
+
+@media (prefers-color-scheme: dark) {
+    .list-item {
+        border-bottom: 1px solid #000 !important;
+    }
+    .nick-name {
+        color: #fff !important;
+    }
+
+    .screen-name {
+        color: #fff !important;
+    }
+
+    .post-content {
+        color: #fff !important;
+    }
+}
+
+.preview-nft {
+    box-sizing: border-box;
+    // position: absolute;
+    // left: 26px;
+    // top: 0px;
+    // z-index: 1999;
+    // display: none;
+
+    .icon-nft-big {
+        width: 300px;
+        height: 300px;
+        object-fit: cover;
+    }
+
+    .content {
+        margin-top: 19px;
+        .nft-name {
+            margin-bottom: 6px;
+            font-weight: 500;
+            font-size: 14px;
+            color: #000;
+        }
+        .nft-desc {
+            font-weight: 400;
+            font-size: 14px;
+            color: #787878;
+            // margin-bottom: 18px;
+        }
+        .nft-date {
+            font-weight: 500;
+            font-size: 12px;
+            color: #ACACAC;
+        }
+    }
+}
+
+.tab-group-page {
+    height: 100%;
+    overflow-y: hidden;
+
+    &::-webkit-scrollbar {
+        width: 2px;
+    }
+    &::-webkit-scrollbar-track {
+        background: rgb(241, 241, 241);
+    }
+    &::-webkit-scrollbar-thumb {
+        background: rgb(136, 136, 136);
+        border-radius: 8px;
+    }
+
+    .list-wrapper {
+
+        .list-item:hover {
+            background: rgba($color: #000000, $alpha: 0.03);
+        }
+
+        .list-item {
+            padding: 20px;
+            box-sizing: border-box;
+            display: flex;
+            border-bottom: 1px solid #F0F3F4;
+            cursor: pointer;
+
+            .left {
+                margin-right: 10px;
+
+                .icon-avatar {
+                    width: 47px;
+                    height: 47px;
+                    border-radius: 50%;
+                }
+            }
+
+            .right {
+                flex: 1;
+                .top {
+                    display: flex;
+                    align-items: center;
+                    margin-bottom: 7px;
+                    position: relative;
+
+                    .icon-nft-wrapper {
+                        height: 24px;
+                        margin-right: 8px;
+                        .icon-nft {
+                            width: 24px;
+                            height: 24px;
+                            // object-fit: cover;
+                        }
+                    }
+
+                    .icon-nft-wrapper:hover {
+                        .preview-nft  {
+                            // display: block;
+                        }
+                    }
+
+                    .nick-name, .screen-name {
+                        font-weight: 600;
+                        font-size: 15px;
+                    }
+
+                    .nick-name {
+                        color: #000;
+                        margin-right: 8px;
+                    }
+
+                    .screen-name {
+                        color: #566370;
+                    }
+                }
+
+                .post-content {
+                    font-weight: 400;
+                    font-size: 16px;
+                    line-height: 24px;
+                    color: rgb(15, 20, 25);
+                    font-family: TwitterChirp, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+                    word-break: break-all;
+                    white-space: pre-line;
+                }
+            }
+        }
+    }
+
+    .icon-loading {
+        width: 26px;
+        height: 26px;  
+        display: block;
+        margin: 20px auto;
+        animation: loading infinite 0.8s linear;
+    }
+}
+
+@keyframes loading {
+    0% {
+        transform: rotate(0);
+    }
+    100% {
+        transform: rotate(360deg);
+    }
+}
+</style>

+ 25 - 2
src/view/popup/components/tabbar.vue

@@ -37,6 +37,7 @@ import { setBadgeInfo, hideBadge } from "@/logic/background/twitter";
 
 import { nftListMine } from "@/http/nft.js";
 import { getAllMessageInfo } from "@/http/messageApi"
+import { getChromeStorage } from "@/uilts/chromeExtension";
 
 
 const props = defineProps({
@@ -129,8 +130,8 @@ const setActiveTab = () => {
     })
 };
 
-const getNFTListMine = () => {
-    if(!props.userInfo.accessToken) return;
+const getNFTListMine = (params = {}) => {
+    if(!props.userInfo.accessToken && !params.accessToken) return;
     nftListMine({
         params: NFTReqParams.params,
     }).then((res) => {
@@ -170,6 +171,7 @@ const msgListener = (req, sender, sendResponse) => {
     switch (req.actionType) {
         case 'CONTENT_POPUP_PAGE_SHOW':
             init();
+            getNFTListMine();
             break;
     }
 }
@@ -179,9 +181,30 @@ const init = () => {
     setMessageCount();
 }
 
+
+const onPageVisbile = () => {
+    document.addEventListener('visibilitychange', function () {
+        let isHidden = document.hidden;
+        if (!isHidden) {
+            setTimeout(() => {
+                getUserInfo((res) => {
+                    getNFTListMine(res);
+                })
+            }, 1200);
+        }
+    });
+}
+
+const getUserInfo = (cb) => {
+    getChromeStorage("userInfo", (res) => {
+        cb && cb(res || {});
+    });
+};
+
 onMounted(() => {
     onMessage();
     init();
+    onPageVisbile()
 });
 
 onBeforeUnmount(() => {

+ 6 - 6
src/view/popup/tabbar-page/more/index.vue

@@ -31,18 +31,18 @@ let moreTabList = ref([
   {
     icon: require("@/assets/svg/icon-website.svg"),
     label: "Official Website",
-    href: "https://denet.me",
+    href: "https://www.denet.me",
   },
   {
     icon: require("@/assets/svg/icon-twitter.svg"),
     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-discord.svg"),
+  //   label: "Discord",
+  //   href: "https://discord.gg/wZSz9p8ddG",
+  // },
   {
       icon: require("@/assets/svg/icon-telegram.svg"),
       label: "Telegram",

+ 27 - 0
src/view/popup/tabbar-page/nft/index.vue

@@ -11,6 +11,16 @@
         <div class="name">{{item.nftItemName}}</div>
       </div>
     </div>
+    <join-group-finish-dialog 
+        :dialogVisible="joinGroupFinishShow"
+        :position="'absolute'" 
+        :contentStyle="{
+          width: '315px',
+        }"
+        :iconStyle="{width: '80px',  marginTop: '26px'}"
+        :descStyle="{marginTop: '24px', marginBottom: '25px', fontSize: '19px'}"
+        @confirm="confirmFinish">
+        </join-group-finish-dialog>
   </div>
 </template>
 
@@ -20,6 +30,8 @@ import router from "@/router/popup.js";
 
 import {nftListMine} from "@/http/nft.js";
 
+import joinGroupFinishDialog from "@/view/components/join-group-finish-dialog.vue";
+
 let listData = ref([]);
 
 let NFTReqParams = {
@@ -32,6 +44,7 @@ let NFTReqParams = {
 
 let pageWrapperDom = ref(null);
 let pageListDom = ref(null);
+let joinGroupFinishShow = ref(false);
 
 const clickNFT = (params) => {
   router.push({
@@ -85,10 +98,24 @@ const msgListener = (req, sender, sendResponse) => {
     switch (req.actionType) {
         case 'CONTENT_POPUP_PAGE_SHOW':
             getNFTListMine();
+            showJoinFinishHandler(req.data);
             break;
     }
 }
 
+const showJoinFinishHandler = (params) => {
+    let { path, showJoinGroupFinish} = params;
+    if(path == '/NFT' && showJoinGroupFinish) {
+      joinGroupFinishShow.value = true;
+    } else if(joinGroupFinishShow.value){
+      joinGroupFinishShow.value = false;
+    }
+}
+
+const confirmFinish = () => {
+  joinGroupFinishShow.value = false;
+}
+
 onMounted(() => {
   onMessage();
   getNFTListMine();

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.