瀏覽代碼

Merge branch 'feature_220615_topics_01' into dev_1.1.1

# Conflicts:
#	src/entry/content.js
#	src/logic/content/nft.js
#	src/logic/content/twitter.js
#	src/manifest.json
wenliming 2 年之前
父節點
當前提交
9e76b6ebd8

二進制
src/assets/img/icon-group-tab-item.png


File diff suppressed because it is too large
+ 10 - 0
src/assets/img/img-pined-guide-new-2.svg


二進制
src/assets/img/img-pined-guide-new.png


File diff suppressed because it is too large
+ 6 - 0
src/assets/svg/icon-celebration.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>

+ 14 - 2
src/entry/content.js

@@ -29,7 +29,11 @@ import {
     showJoinDialog,
     showTwitterPost,
     setTwitterTextarea,
-    showGroupTip
+    showGroupTip,
+    setTabGroupIframeStyle,
+    loginSuccessHandle,
+    pageJumpHandler,
+    getTweetProfileNavTop
 } from "@/logic/content/twitter.js";
 
 import { 
@@ -96,7 +100,6 @@ window.onmessage = (res) => {
 
 
 chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
-    sendResponse('')
     switch (req.actionType) {
         case 'BG_SHOW_PIN_TIPS':
             showPinTips()
@@ -136,6 +139,15 @@ 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;

+ 17 - 0
src/http/nft.js

@@ -55,3 +55,20 @@ export function prePost(params) {
         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
+    })
+}

+ 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');

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

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

+ 1 - 1
src/logic/content/nft.js

@@ -142,4 +142,4 @@ export const elemAddEventListener = (elem, action, fn) => {
     if (elem) {
         elem.addEventListener(action, fn)
     }
-}
+}

+ 310 - 7
src/logic/content/twitter.js

@@ -4,7 +4,7 @@ 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 } from '@/logic/content/nft';
+import { showNFTGroupIcon, hideNFTGroupList, checkUserJoinGroup, elemAddEventListener, addJoinedGroupList } from '@/logic/content/nft';
 
 let dom = {};
 
@@ -773,6 +773,8 @@ function initParseCard() {
                 changeQueueNum(-1)
                 showNFTCard()
                 initGroupTip()
+                addGroupTab();
+                addJoinedGroupList();
             }, 1000)
         } else if (inFacebook && inFacebookNode) {
             clearInterval(timer)
@@ -822,8 +824,9 @@ export function init() {
     checkUserJoinGroup();
     renderDom();
     checkTwitterTaskState();
-    onBodyClick();
     initBuyNFT();
+    addGroupTab();
+    addJoinedGroupList();
 
     getChromeStorage("popupShowPublishDialog", (res) => {
         console.log("popupShowPublishDialog", res);
@@ -1501,7 +1504,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();
@@ -1517,9 +1520,12 @@ export const showPopupPage = (params = {}) => {
         }
         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({
@@ -1603,7 +1609,6 @@ const onBodyClick = () => {
     }
 }
 
-
 export const setPopupConfByPopupPage = () => {
     let iframe = document.getElementById('de-popup-page');
     if (iframe) {
@@ -1634,10 +1639,308 @@ export const setPopupConfByPopupPage = () => {
     }
 }
 
+
+/** 
+ * 
+ * Group Tab List Start 
+ */
+
+const addGroupTab = () => {
+    let tabListDom = document.querySelector('div[role="tablist"]');
+    let groupItemTab = document.querySelector('#de-nav-tab-group');
+
+    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';
+    divNode.appendChild(groupIcon);
+    divNode.appendChild(document.createTextNode('Group'));
+
+    if(tabListDom && !groupItemTab) {
+        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);
+        tabListDom.appendChild(groupTab);
+
+        groupTab.addEventListener('mouseenter', function() {
+            groupTab.style.background = 'rgba(15, 20, 25, 0.1)'
+        });
+        groupTab.addEventListener('mouseleave', function() {
+            groupTab.style.background = 'none'
+        });
+
+        addGroupTabEventListener({groupTab});
+    }
+    
+    addTweetTabEventListener({
+        tabListDom
+    });
+
+    addTabGroupContent();
+    checkNeedSelectGroupTab();
+}
+
+/**
+ * 跳转到个人主页 检查是否需要选中 Group tab
+ */
+const checkNeedSelectGroupTab = () => {
+    if(window.location.pathname != '/home') {
+        setTimeout(() => {
+            const urlParams = new URLSearchParams(window.location.search);
+            const deTabVal = urlParams.get('deTabVal');
+            if (deTabVal == 'deGroupTab') {
+                selectGroupTab();
+            }
+        }, 500)
+    }
+}
+
+/** 选中 Group tab */
+const selectGroupTab = () => {
+    let groupTab = document.querySelector('#de-nav-tab-group');
+    if(groupTab) {
+        groupTab.click();
+    }
+};
+
+/**
+ * 
+ * Group tab点击事件监听
+ */
+const addGroupTabEventListener = (params) => {
+    let {groupTab} = params;
+    groupTab.addEventListener('click', function() {
+        setGroupTabSelfStyle({groupColor: 'rgb(15, 20, 25)', 
+            groupFontWeight: '700', 
+            lineDisplay: 'block'});
+        
+        setTweetActiveTabStyle({
+            color: 'rgb(83, 100, 113)', 
+            display: 'none'});
+
+        setTabContentStyle({
+            tweetTabContentDisply: 'none', 
+            iframeContentDisplay: 'block'});
+
+        refreshTabGroup();
+
+        window.addEventListener('scroll', addPageScrollEvent) 
+    })
+}
+
+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}, () => {});
+};
+
+/**
+ * 
+ * twitter tab点击事件监听
+ */
+const addTweetTabEventListener = (params) => {
+    let {tabListDom} = params;
+    let  groupItemTab = document.querySelector('#de-nav-tab-group');
+
+    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', function() {
+                    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'});
+                })
+            }
+        }
+    }
+}
+
+/**
+ * 设置 Group Tab 样式
+ */
+const setGroupTabSelfStyle = (params = {}) => {
+    let {groupColor, groupFontWeight, lineDisplay} = params;
+    let  groupTab = document.querySelector('#de-nav-tab-group');
+    groupTab.style.color = groupColor;
+    groupTab.style.fontWeight = groupFontWeight;
+
+    let lineDom = groupTab.querySelector('#de-tab-line');
+    if(lineDom) {
+        lineDom.style.display = lineDisplay;
+    }
+};
+
+/**
+ * 切换到 Group tab时 刷新列表
+ */
+const refreshTabGroup = () => {
+    chrome.runtime.sendMessage({ actionType: "CONTENT_REFRESH_TAB_GROUP_LIST", 
+        data:{}}, () => { });
+}
+
+/**
+ * 
+ * tab选中时设置 激活 的字体样式和选中条
+ */
+const setTweetActiveTabStyle = (params) => {
+    let {color, display} = params || {};
+    let tweetActiveTab = document.querySelector('a[aria-selected="true"]').querySelector('div');
+    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 {display} = tweetTabContent.style;
+            if(display == 'none') {
+                tweetTabContent.style.display = 'block';
+            } 
+        } else {
+            tweetTabContent.style.display = tweetTabContentDisply;
+        }
+    }
+
+    let iframeContent = document.getElementById('de-tab-group-content');
+
+    if(!iframeContent) {
+        addTabGroupContent();
+    }
+    setTimeout(() => {
+        iframeContent = document.getElementById('de-tab-group-content');
+        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 = () => {
+    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 = document.getElementById('de-tab-group-content');
+
+    let tweetTabContent = getTweetTabContent();
+    if (!iframeContent) {
+        if(tweetTabContent && tweetTabContent.parentElement) {
+            tweetTabContent.parentElement.appendChild(iframe);
+        }
+    }
+};
+
+/**
+ * 
+ * 设置Tab Group Iframe 样式
+ */
+export const setTabGroupIframeStyle = (params) => {
+    let iframeContent = document.getElementById('de-tab-group-content');
+    iframeContent.style.height =  document.querySelector('html').offsetHeight + 'px';
+}
+
+export const pageJumpHandler = (params) => {
+    let {url, name = '_self'} = params
+    window.open(url, name)
+}
+
+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
+    } }, () => { })
+} 
+
+/** 
+ * 
+ * Group Tab List End 
+ * 
+ */
+
+
 export const loginSuccessHandle = () => {
     // 检查是否漏出group图标
     checkUserJoinGroup(() => {
         showNFTGroupIcon()
         addEventAction()
+        addJoinedGroupList();
     })
 }

+ 4 - 1
src/manifest.json

@@ -72,7 +72,10 @@
                 "/iframe/nft-group-card.html",
                 "/iframe/buy-nft.html",
                 "/iframe/group-card.html",
-                "/iframe/popup-page.html"
+                "/iframe/popup-page.html",
+                "/iframe/popup-page.html",
+                "/iframe/tab-group.html",
+                "/iframe/joined-group-list.html"
             ],
             "matches": [
                 "<all_urls>"

+ 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>

+ 38 - 2
src/view/components/nft-group-list.vue

@@ -5,16 +5,28 @@
             :key="index"
             v-for="(item, index) in detail"
             @click="clickHandler(item)">
-            <div class="logo"><img :src="item.nftGroupIcon" /></div>
+            <div class="logo">
+                <img :src="item.nftGroupIcon" />
+                <div class="badge" v-if="showBadge">
+                    {{item.newPostCount}}
+                </div>
+            </div>
             <div class="text">{{item.nftGroupName}}</div>
         </div>
     </div>
 </template>
 
 <script setup>
-import { onBeforeMount, ref, defineEmits } from 'vue'
+import { onBeforeMount, ref, defineEmits, defineProps } 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);
@@ -47,6 +59,7 @@ body {
 }
 
 .list {
+    overflow-y: auto;
     .item {
         display: flex;
         height: 48px;
@@ -61,11 +74,34 @@ body {
             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;

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

@@ -0,0 +1,77 @@
+<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%" :showBadge="true" @clickCallBack="clickHandler"></nft-group-list>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { onMounted, ref, watch } from "vue";
+import NftGroupList from '@/view/components/nft-group-list.vue';
+
+const clickHandler = (params) => {
+    let url = `https://twitter.com/${params.defaultTwitterAccount}?deTabVal=deGroupTab`;
+    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) });
+    })
+}
+
+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>

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

@@ -0,0 +1,337 @@
+<template>
+    <div class="tab-group-page" ref="pageWrapperDom" @scroll="pageScroll">
+        <div class="list-wrapper" ref="listWrapperDom">
+            <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">
+                            <img :src="item.nftItem.imagePath" class="icon-nft">
+                            <div class="preview-nft">
+                                <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-html="item.nftItem.metadata.description"></div>
+                                    </div>
+                                    <!-- <div class="nft-date">
+                                        {{item.nftItem.dateOfPossession}}
+                                    </div> -->
+                                </div>
+                            </div>
+                        </div>
+                        <div class="nick-name">
+                            {{item.nickName}}{{index}}
+                        </div>
+                        <div class="screen-name">
+                            {{item.screenName}}
+                        </div>
+                    </div>
+                    <div class="post-content" v-html="item.textContent"></div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { onMounted, ref } from "vue";
+import { getGroupPostList, getTwitterNftGroupInfo } from '@/http/nft'
+import { getQueryString } from '@/uilts/help.js'
+
+let twitterAccount = '';
+let groupInfo = {};
+
+let listData = ref([])
+let listWrapperDom = ref(null);
+let pageWrapperDom = ref(null);
+
+let listReqParams = {
+    params: {
+        pageSize: 100,
+        preTimestamp: ''
+    },
+    loadMore: false,
+};
+
+const clickItem = (data, index) => {
+    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':
+                setListData();
+                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) => {
+    console.log('pageScroll',e.target.scrollTop)
+    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;
+    console.log(wrapperHeight,scrollTop,  '---', listContentHeight - 100)
+    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 => {
+        if (res.code == 0) {
+            let resData = res.data;
+            if (resData.length) {
+                if (!listReqParams.params.preTimestamp) {
+                    listData.value = resData;
+                } else {
+                    let data = dataList.value;
+                    data = data.concat(resData);
+                    listData.value = data;
+                }
+                listReqParams.loadMore = false;
+            }
+        }
+    })
+}
+
+onMounted(() => {
+    onRuntimeMsg();
+    let {windowLocation} = JSON.parse(getQueryString('params'));
+    console.log('windowLocation', windowLocation)
+    if(windowLocation.pathname) {
+        twitterAccount = windowLocation.pathname.split('/')[1];
+        if(twitterAccount) {
+            getTwitterNftGroupInfo({
+                params: {
+                    twitterAccount
+                }
+            }).then(res => {
+                if(res.code == 0) {
+                    groupInfo = res.data;
+                    getListData()
+                }
+            })
+        }
+    }
+
+
+    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;
+}
+.tab-group-page {
+    height: 100%;
+    overflow-y: auto;
+
+    &::-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;
+                    }
+
+                    .preview-nft {
+                        width: 340px;
+                        background: #FFFFFF;
+                        box-shadow: 0px 4px 20px rgba(0, 0, 0, 0.2);
+                        border-radius: 20px;
+                        padding: 20px;
+                        box-sizing: border-box;
+                        position: absolute;
+                        left: 28px;
+                        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;
+                            }
+                            .nft-desc {
+                                // margin-bottom: 18px;
+                            }
+                            .nft-date {
+                                font-weight: 500;
+                                font-size: 12px;
+                                color: #ACACAC;
+                            }
+                        }
+                    }
+                }
+
+                .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;
+                }
+            }
+        }
+    }
+}
+</style>

+ 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();

Some files were not shown because too many files changed in this diff