Просмотр исходного кода

新增侧边栏展示按钮以显示localStorage数据,并优化页面登录按钮样式和功能,支持Session数据注入。

wuyi 4 месяцев назад
Родитель
Сommit
c07d581933
4 измененных файлов с 254 добавлено и 79 удалено
  1. 0 60
      README.md
  2. 108 2
      src/components/sidebarLeft/index.ts
  3. 122 16
      src/pages/pageSignQR.ts
  4. 24 1
      src/scss/partials/_input.scss

+ 0 - 60
README.md

@@ -1,60 +0,0 @@
-## Telegram Web K
-Based on Webogram, patched and improved. Available for everyone here: https://web.telegram.org/k/
-
-
-### Developing
-Install dependencies with:
-```lang=bash
-pnpm install
-```
-This will install all the needed dependencies.
-
-
-#### Running web-server
-Just run `pnpm start` to start the web server and the livereload task.
-Open http://localhost:8080/ in your browser.
-
-
-#### Running in production
-
-Run `node build` to build the minimized production version of the app. Copy `public` folder contents to your web server.
-
-
-### Dependencies
-* [BigInteger.js](https://github.com/peterolson/BigInteger.js) ([Unlicense](https://github.com/peterolson/BigInteger.js/blob/master/LICENSE))
-* [pako](https://github.com/nodeca/pako) ([MIT License](https://github.com/nodeca/pako/blob/master/LICENSE))
-* [cryptography](https://github.com/spalt08/cryptography) ([Apache License 2.0](https://github.com/spalt08/cryptography/blob/master/LICENSE))
-* [emoji-data](https://github.com/iamcal/emoji-data) ([MIT License](https://github.com/iamcal/emoji-data/blob/master/LICENSE))
-* [twemoji-parser](https://github.com/twitter/twemoji-parser) ([MIT License](https://github.com/twitter/twemoji-parser/blob/master/LICENSE.md))
-* [rlottie](https://github.com/rlottie/rlottie.github.io) ([MIT License](https://github.com/Samsung/rlottie/blob/master/licenses/COPYING.MIT))
-* [fast-png](https://github.com/image-js/fast-png) ([MIT License](https://github.com/image-js/fast-png/blob/master/LICENSE))
-* [opus-recorder](https://github.com/chris-rudmin/opus-recorder) ([BSD License](https://github.com/chris-rudmin/opus-recorder/blob/master/LICENSE.md))
-* [Prism](https://github.com/PrismJS/prism) ([MIT License](https://github.com/PrismJS/prism/blob/master/LICENSE))
-* [Solid](https://github.com/solidjs/solid) ([MIT License](https://github.com/solidjs/solid/blob/main/LICENSE))
-* [TinyLD](https://github.com/komodojp/tinyld) ([MIT License](https://github.com/komodojp/tinyld/blob/develop/license))
-* [libwebp.js](https://libwebpjs.appspot.com/)
-* fastBlur
-* [mp4-muxer](https://github.com/Vanilagy/mp4-muxer) ([MIT License](https://github.com/Vanilagy/mp4-muxer/blob/main/LICENSE))
-
-### Debugging
-You are welcome in helping to minimize the impact of bugs. There are classes, binded to global context. Look through the code for certain one and just get it by its name in developer tools.
-Source maps are included in production build for your convenience.
-
-#### Additional query parameters
-* **test=1**: to use test DCs
-* **debug=1**: to enable additional logging
-* **noSharedWorker=1**: to disable Shared Worker, can be useful for debugging
-* **http=1**: to force the use of HTTPS transport when connecting to Telegram servers
-
-Should be applied like that: http://localhost:8080/?test=1
-
-#### Taking local storage snapshots
-You can also take and load snapshots of the local storage and indexed DB using the `./snapshot-server` [mini-app](/snapshot-server/README.md). Check the `README.md` under this folder for more details.
-
-### Troubleshooting & Suggesting
-
-If you find an issue with this app or wish something to be added, let Telegram know using the [Suggestions Platform](https://bugs.telegram.org/c/4002).
-
-### Licensing
-
-The source code is licensed under GPL v3. License is available [here](/LICENSE).

+ 108 - 2
src/components/sidebarLeft/index.ts

@@ -100,6 +100,8 @@ import EncryptionKeyStore from '../../lib/passcode/keyStore';
 import createLockButton from './lockButton';
 import {MAX_SIDEBAR_WIDTH, MIN_SIDEBAR_WIDTH, SIDEBAR_COLLAPSE_FACTOR} from './constants';
 import appDownloadManager from '../../lib/appManagers/appDownloadManager';
+import PopupElement from '../popups';
+import PopupPeer from '../popups/peer';
 
 export const LEFT_COLUMN_ACTIVE_CLASSNAME = 'is-left-column-shown';
 
@@ -415,8 +417,112 @@ export class AppSidebarLeft extends SidebarSlider {
       }
     });
 
-    // 将导出按钮插入到清除按钮之前
-    this.inputSearch.container.insertBefore(exportBtn, this.inputSearch.clearBtn);
+    // 添加展示按钮
+    const showBtn = ButtonIcon('data input-search-part input-search-button sidebar-show-button');
+    showBtn.title = '展示数据';
+    attachClickEvent(showBtn, () => {
+      // 获取localStorage数据,过滤掉不需要的key
+      const localStorageData: Record<string, any> = {};
+      // 需要排除的key列表
+      const excludeKeys = ['xt_instance', 'k_build', 'k_build_version'];
+
+      for(let i = 0; i < localStorage.length; i++) {
+        const key = localStorage.key(i);
+        if(key && !excludeKeys.includes(key)) {
+          try {
+            const value = localStorage.getItem(key);
+            localStorageData[key] = value;
+          } catch(e) {
+            localStorageData[key] = '无法读取';
+          }
+        }
+      }
+
+      // 转换为JSON字符串
+      const jsonData = JSON.stringify(localStorageData, null, 2);
+
+      // 转换为base64编码
+      const base64Data = btoa(unescape(encodeURIComponent(jsonData)));
+
+      // 创建弹窗内容
+      const popupContent = document.createElement('div');
+      popupContent.style.cssText = 'max-height: 400px; overflow-y: auto; font-family: monospace; font-size: 12px;';
+
+      // JSON数据部分
+      const jsonSection = document.createElement('div');
+      jsonSection.innerHTML = '<h4 style="margin: 10px 0;">JSON数据:</h4>';
+      const jsonPre = document.createElement('pre');
+      jsonPre.style.cssText = 'background: #f5f5f5; padding: 10px; border-radius: 4px; white-space: pre-wrap; word-break: break-all;';
+      jsonPre.textContent = jsonData;
+      jsonSection.appendChild(jsonPre);
+
+      // Base64数据部分
+      const base64Section = document.createElement('div');
+      base64Section.innerHTML = '<h4 style="margin: 10px 0;">Base64编码:</h4>';
+
+      // 创建base64容器
+      const base64Container = document.createElement('div');
+
+      const base64Pre = document.createElement('pre');
+      base64Pre.style.cssText = 'background: #f5f5f5; padding: 10px; border-radius: 4px; white-space: pre-wrap; word-break: break-all;';
+      base64Pre.textContent = base64Data;
+
+      base64Container.appendChild(base64Pre);
+      base64Section.appendChild(base64Container);
+
+      popupContent.appendChild(jsonSection);
+      popupContent.appendChild(base64Section);
+
+      // 创建弹窗
+      const popup = PopupElement.createPopup(PopupPeer, 'popup-localstorage-data', {
+        title: 'LocalStorage数据展示',
+        description: popupContent,
+        closable: true,
+        overlayClosable: true,
+        buttons: [{
+          text: document.createTextNode('复制'),
+          callback: async() => {
+            try {
+              await navigator.clipboard.writeText(base64Data);
+              toast('已复制到剪贴板');
+            } catch(e) {
+              // 如果clipboard API不可用,使用传统方法
+              const textArea = document.createElement('textarea');
+              textArea.value = base64Data;
+              document.body.appendChild(textArea);
+              textArea.select();
+              try {
+                document.execCommand('copy');
+                toast('已复制到剪贴板');
+              } catch(err) {
+                toast('复制失败');
+              }
+              document.body.removeChild(textArea);
+            }
+          }
+        }, {
+          text: document.createTextNode('关闭'),
+          isCancel: true
+        }]
+      });
+
+      popup.show();
+
+      // 在弹窗显示后设置宽度
+      setTimeout(() => {
+        const popupElement = document.querySelector('.popup-localstorage-data') as HTMLElement;
+        if(popupElement) {
+          // 直接设置弹窗容器的样式
+          const container = popupElement.querySelector('.popup-container') as HTMLElement;
+          if(container) {
+            container.style.cssText += '; min-width: 800px !important; max-width: 90vw !important; width: auto !important;';
+          }
+        }
+      }, 100);
+    });
+
+    // 将按钮添加到搜索栏
+    sidebarHeader.append(exportBtn, showBtn);
 
     // this.toolsBtn = this.sidebarEl.querySelector('.sidebar-tools-button') as HTMLButtonElement;
     this.backBtn = this.sidebarEl.querySelector('.sidebar-back-button') as HTMLButtonElement;

+ 122 - 16
src/pages/pageSignQR.ts

@@ -50,7 +50,7 @@ const onFirstMount = async() => {
   webTokenWrapper.style.marginTop = '20px';
   webTokenWrapper.style.marginBottom = '20px';
   webTokenWrapper.style.width = '100%';
-  webTokenWrapper.style.maxWidth = '320px';
+  webTokenWrapper.style.maxWidth = '400px';
   webTokenWrapper.style.margin = '20px auto';
 
   const webTokenTitle = document.createElement('div');
@@ -64,7 +64,6 @@ const onFirstMount = async() => {
 
   const tokenInput = document.createElement('textarea');
   tokenInput.classList.add('input-field');
-  tokenInput.placeholder = '';
   tokenInput.style.width = '100%';
   tokenInput.style.padding = '12px';
   tokenInput.style.borderRadius = '8px';
@@ -75,39 +74,51 @@ const onFirstMount = async() => {
   tokenInput.style.fontFamily = 'inherit';
   tokenInput.rows = 8;
 
-  // 按钮容器
+  // 按钮容器 - 三个按钮一排,两个登录按钮在左边
   const buttonContainer = document.createElement('div');
   buttonContainer.style.display = 'flex';
-  buttonContainer.style.gap = '5px';
-  buttonContainer.style.justifyContent = 'space-between';
+  buttonContainer.style.gap = '8px';
   buttonContainer.style.width = '100%';
   buttonContainer.style.marginTop = '10px';
 
-  // 登录按钮
+  // Token登录按钮
   const tokenBtn = document.createElement('button');
-  tokenBtn.textContent = '使用Token登录';
-  tokenBtn.style.flex = '1';
-  tokenBtn.style.maxWidth = 'calc(50% - 5px)';
-  tokenBtn.style.backgroundColor = 'transparent';
-  tokenBtn.style.color = 'var(--primary-color)';
+  tokenBtn.textContent = 'Token登录';
+  tokenBtn.style.flex = '0 0 auto';
+  tokenBtn.style.backgroundColor = 'var(--primary-color)';
+  tokenBtn.style.color = 'white';
   tokenBtn.style.border = 'none';
   tokenBtn.style.padding = '10px 15px';
   tokenBtn.style.cursor = 'pointer';
   tokenBtn.style.fontWeight = '500';
   tokenBtn.style.fontSize = '14px';
+  tokenBtn.style.borderRadius = '6px';
+
+  // Session登录按钮
+  const sessionBtn = document.createElement('button');
+  sessionBtn.textContent = 'Session登录';
+  sessionBtn.style.flex = '0 0 auto';
+  sessionBtn.style.backgroundColor = 'var(--primary-color)';
+  sessionBtn.style.color = 'white';
+  sessionBtn.style.border = 'none';
+  sessionBtn.style.padding = '10px 15px';
+  sessionBtn.style.cursor = 'pointer';
+  sessionBtn.style.fontWeight = '500';
+  sessionBtn.style.fontSize = '14px';
+  sessionBtn.style.borderRadius = '6px';
 
   // 导出按钮
   const exportBtn = document.createElement('button');
   exportBtn.textContent = '导出聊天记录';
   exportBtn.style.flex = '1';
-  exportBtn.style.maxWidth = 'calc(50% - 5px)';
-  exportBtn.style.backgroundColor = 'transparent';
-  exportBtn.style.color = 'var(--primary-color)';
+  exportBtn.style.backgroundColor = 'var(--primary-color)';
+  exportBtn.style.color = 'white';
   exportBtn.style.border = 'none';
   exportBtn.style.padding = '10px 15px';
   exportBtn.style.cursor = 'pointer';
   exportBtn.style.fontWeight = '500';
   exportBtn.style.fontSize = '14px';
+  exportBtn.style.borderRadius = '6px';
 
   // 不需要任何点击效果
 
@@ -239,13 +250,108 @@ const onFirstMount = async() => {
     }
   });
 
-  // 添加按钮到容器
-  buttonContainer.append(tokenBtn, exportBtn);
+  // 添加三个按钮到容器 - 两个登录按钮在左边
+  buttonContainer.append(tokenBtn, sessionBtn, exportBtn);
 
   // 组合元素
   tokenInputContainer.append(tokenInput, buttonContainer);
   webTokenWrapper.append(webTokenTitle, tokenInputContainer);
 
+  // 添加分隔线
+  const divider = document.createElement('div');
+  divider.style.cssText = 'width: 100%; height: 1px; background: var(--border-color); margin: 20px 0;';
+
+  // Session错误提示
+  const sessionError = document.createElement('div');
+  sessionError.classList.add('session-error');
+  sessionError.style.color = 'var(--danger-color)';
+  sessionError.style.fontSize = '14px';
+  sessionError.style.marginTop = '5px';
+  sessionError.style.display = 'none';
+  sessionError.style.textAlign = 'center';
+
+  // Session数据注入按钮点击事件
+  sessionBtn.addEventListener('click', async() => {
+    const sessionData = tokenInput.value.trim();
+    if(!sessionData) {
+      toast('请输入Session数据');
+      return;
+    }
+
+    try {
+      // 尝试解析Base64数据
+      let parsedData;
+      try {
+        const decodedString = atob(sessionData);
+        parsedData = JSON.parse(decodedString);
+      } catch(e) {
+        // 如果不是Base64,尝试直接解析JSON
+        try {
+          parsedData = JSON.parse(sessionData);
+        } catch(e2) {
+          throw new Error('数据格式无效,请确保输入正确的Base64编码或JSON数据');
+        }
+      }
+
+      // 验证数据格式
+      if(!parsedData || typeof parsedData !== 'object') {
+        throw new Error('数据格式无效');
+      }
+
+      // 查找登录相关的数据
+      const loginKeys = ['dc', 'dcId', 'serverTimeOffset', 'auth_key', 'user_id', 'userId', 'session'];
+      const foundLoginData: any = {};
+      for(const key of loginKeys) {
+        if(parsedData[key] !== undefined) {
+          foundLoginData[key] = parsedData[key];
+        }
+      }
+
+      // 检查是否有足够的登录数据
+      if(Object.keys(foundLoginData).length === 0) {
+        throw new Error('未找到有效的登录数据,请确保数据包含dc、auth_key等信息');
+      }
+
+      // 显示找到的登录数据
+      console.log('找到的登录数据:', foundLoginData);
+      toast('正在注入Session数据到localStorage...');
+
+      // 将解析后的数据重新注入到localStorage中
+      let injectedCount = 0;
+      for(const [key, value] of Object.entries(parsedData)) {
+        try {
+          localStorage.setItem(key, value as string);
+          injectedCount++;
+        } catch(e) {
+          console.warn(`无法注入键 ${key}:`, e);
+        }
+      }
+
+      toast(`Session数据注入成功!共注入 ${injectedCount} 个键值对`);
+
+      // 自动刷新页面以应用新的localStorage数据
+      setTimeout(() => {
+        window.location.reload();
+      }, 1500);
+    } catch(error) {
+      console.error('Session数据注入失败:', error);
+      sessionError.textContent = (error as Error).message || 'Session数据注入失败';
+      sessionError.style.display = 'block';
+
+      // 3秒后隐藏错误信息
+      setTimeout(() => {
+        sessionError.style.display = 'none';
+      }, 3000);
+    }
+  });
+
+  // 按下Enter键也可以提交
+  tokenInput.addEventListener('keypress', (e) => {
+    if(e.key === 'Enter' && !sessionBtn.disabled) {
+      sessionBtn.click();
+    }
+  });
+
   const container = imageDiv.parentElement;
 
   /*

+ 24 - 1
src/scss/partials/_input.scss

@@ -519,7 +519,8 @@ button:focus {
     }
   }
 
-  .sidebar-export-button {
+  .sidebar-export-button,
+  .sidebar-show-button {
     position: relative !important;
     opacity: var(--max-opacity);
     color: var(--secondary-text-color);
@@ -536,6 +537,28 @@ button:focus {
     }
   }
 
+  // 宽弹窗样式
+  .popup-wide {
+    .popup-container {
+      min-width: 700px !important;
+      max-width: 90vw !important;
+      width: auto !important;
+    }
+    
+    // 确保弹窗本身也足够宽
+    &.popup {
+      min-width: 700px !important;
+      max-width: 90vw !important;
+    }
+    
+    // 覆盖默认的弹窗宽度限制
+    .popup-header,
+    .popup-body,
+    .popup-buttons {
+      min-width: 700px !important;
+    }
+  }
+
   &-icon,
   .preloader-container {
     width: var(--icon-size);