Przeglądaj źródła

[feature] render group list

wenliming 2 lat temu
rodzic
commit
6de3c0833f

+ 13 - 2
src/entry/content.js

@@ -24,7 +24,8 @@ import {
     showBuyNFT,
     hideBuyNFT,
     showPopupPage,
-    setPopupConfByPopupPage
+    setPopupConfByPopupPage,
+    setTabGroupIframeStyle
 } from "@/logic/content/twitter.js";
 
 import {
@@ -86,7 +87,6 @@ window.onmessage = (res) => {
 
 
 chrome.runtime.onMessage.addListener((req, sender, sendResponse) => {
-    sendResponse('')
     switch (req.actionType) {
         case 'BG_SHOW_PIN_TIPS':
             showPinTips()
@@ -120,6 +120,17 @@ 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':
+            let top = document.querySelector('nav[aria-label="Profile timelines"]').getBoundingClientRect().top;
+            chrome.runtime.sendMessage({ actionType: "CONTENT_SEND_GROUP_NAV_TOP", data: {
+                top,
+                scrollTop: req.data.scrollTop
+            } }, () => { })
+            break;
     }
 })
 

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

+ 249 - 11
src/logic/content/twitter.js

@@ -765,6 +765,7 @@ function initParseCard() {
                 checkHasSliderDeBtn();
                 changeQueueNum(-1)
                 showNFTCard()
+                addGroupTab();
             }, 1000)
         } else if (inFacebook && inFacebookNode) {
             clearInterval(timer)
@@ -812,8 +813,8 @@ export function init() {
     renderDom();
     checkTwitterTaskState();
 
-    onBodyClick();
     initBuyNFT();
+    addGroupTab();
 
     getChromeStorage("popupShowPublishDialog", (res) => {
         console.log("popupShowPublishDialog", res);
@@ -1433,16 +1434,6 @@ export const tiggerInjectPopupPage = () => {
     }
 }
 
-const onBodyClick = () => {
-    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) {
@@ -1471,4 +1462,251 @@ export const setPopupConfByPopupPage = () => {
             } 
         }, () => { });
     }
+}
+
+
+const addGroupTab = () => {
+    let tabListDom = document.querySelector('div[role="tablist"]');
+    let groupItemTab = document.querySelector('#de-nav-tab-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.innerHTML = `Group`;
+        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(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();
+}
+
+/**
+ * 
+ * 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;
+        }
+    } else {
+        tweetTabContent = document.querySelector('div[data-testid="emptyState"]');
+        if(tweetTabContent) {
+            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"])');
+    return tweetTabContent;
+}
+
+/**
+ * 注入 Group List 内容
+ */
+const addTabGroupContent = () => {
+    let iframe = document.createElement('iframe');
+        iframe.id = 'de-tab-group-content';
+        iframe.src = chrome.runtime.getURL('/iframe/tab-group.html');
+        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);
+        } else {
+            tweetTabContent = document.querySelector('div[data-testid="emptyState"]');
+            if(tweetTabContent && tweetTabContent.parentElement) {
+                tweetTabContent.parentElement.appendChild(iframe);
+            }
+        }
+    }
+};
+
+/**
+ * 
+ * 设置Tab Group Iframe 样式
+ */
+export const setTabGroupIframeStyle = (params) => {
+    let {height = 0} = params;
+    // if(height > 0) {
+        let iframeContent = document.getElementById('de-tab-group-content');
+        // iframeContent.style.height = height + 'px';
+        iframeContent.style.height =  document.querySelector('html').offsetHeight + 'px';
+    // }
 }

+ 2 - 1
src/manifest.json

@@ -69,7 +69,8 @@
                 "/iframe/bind-tweet.html",
                 "/iframe/nft-card.html",
                 "/iframe/buy-nft.html",
-                "/iframe/popup-page.html"
+                "/iframe/popup-page.html",
+                "/iframe/tab-group.html"
             ],
             "matches": [
                 "<all_urls>"

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

@@ -0,0 +1,364 @@
+<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">
+                        {{item.textContent}}
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+</template>
+
+<script setup>
+import { onMounted, ref, watch } from "vue";
+let listData = ref([])
+let listWrapperDom = ref(null);
+let pageWrapperDom = ref(null);
+
+let listReqParams = {
+    params: {
+        pageNum: 1,
+        pageSize: 100,
+    },
+    loadMore: false,
+};
+
+const clickItem = (data, index) => {
+    let url = `twitter.com/${data.screenName}/status/${data.srcContentId}`;
+}
+
+watch(
+    () => listData,
+    (newVal) => {
+        setTimeout(() => {
+            console.log(listWrapperDom.value.offsetHeight);
+            if(listWrapperDom.value && listWrapperDom.value.offsetHeight > 1) {
+                
+            }
+        }, 500)
+    },
+    {
+        deep: true
+    }
+);
+
+let itemData = {
+      "avatarUrl": "https://pbs.twimg.com/profile_images/1534854907836043266/gVFuSGKE_400x400.jpg",
+      "createTimestamp": 0,
+      nftItem: {
+        "dateOfPossession": "string",
+        "details": {
+          "blockChain": "string",
+          "contractAddress": "string",
+          "contractAddressUrl": "string",
+          "creatorFees": "string",
+          "tokenId": "string",
+          "tokenIdUrl": "string",
+          "tokenStandard": "string",
+          "transactionRoyalties": "string"
+        },
+        "imagePath": "https://img2.baidu.com/it/u=2343388762,2916277035&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500",
+        "metadata": {
+            description: '<div>desc</div>'
+        },
+        "nftItemId": 0,
+        "nftItemName": "string",
+        "nftProjectId": "string",
+        "purchasePrice": "string",
+        "saleCurrencyCode": "string",
+        "salePrice": "string"
+      },
+      "nickName": "string",
+      "postId": "string",
+      "screenName": "string",
+      "srcContentId": "string",
+      "textContent": `罗永浩 72 年的,就比我大两岁
+
+看到他演完「真还传」又要去演「最美不过夕阳红」
+
+还是很感动的
+
+于是我也决定淡推几个月
+
+新的事情没赚钱之前,先低调点
+
+各位保重,等我赚到钱回来教大家`,
+      "uid": 0
+    };
+
+const addData = () => {
+    setTimeout(() => {
+        let list = [];
+        for(let i = 0; i < 50; i++){
+            list.push(itemData)
+        }
+        let data = listData.value.concat(list);
+        listData.value = data;
+    }, 800)
+}
+
+const setListData = () => {
+    setTimeout(() => {
+        let list = [];
+        for(let i = 0; i < 50; i++){
+            list.push(itemData)
+        }
+        listData.value = list;
+    }, 800)
+}
+
+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;
+    // console.log(wrapperHeight+wrapperScrollTop, contentHeight,  wrapperHeight + wrapperScrollTop >= (contentHeight - 100))
+    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)
+    ) {
+        console.log('next---111');
+        listReqParams.loadMore = true;
+        listReqParams.params.pageNum++;
+        addData();
+        // getCurrencyInfoList();
+    }
+}
+
+const sendMessageToContent = (params) => {
+    let {actionType, data} = params || {};
+    chrome.tabs.getCurrent((tab) => {
+        chrome.tabs.sendMessage(tab.id, {
+        actionType,
+        data,
+        }, (res) => { console.log(res) });
+    })
+}
+
+onMounted(() => {
+    onRuntimeMsg();
+    setListData();
+
+    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;
+                }
+            }
+        }
+    }
+}
+</style>