├── .eslintrc ├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── CSSSAMPLE.md ├── ISSUE_TEMPLATE.md ├── LICENSE.md ├── README.md ├── README_en.md ├── assets ├── 256x256.png ├── close_white.png ├── exit_black.png ├── exit_white.png ├── fullscreen-exit.png ├── fullscreen.png ├── icon.icns ├── icon.png ├── loading.gif ├── settings_black.png ├── settings_white.png ├── status_bar.png ├── status_bar@2x.png ├── tray_black.png ├── tray_exit_black.png ├── tray_exit_white.png ├── tray_icon.png ├── tray_settings_black.png ├── tray_settings_white.png ├── tray_unread_black.png ├── tray_unread_white.png ├── tray_white.png └── vertical-align-botto.png ├── config.json ├── electronic-wechat.desktop ├── package.json ├── scripts ├── build-all.sh ├── build-win32.bat ├── build.sh ├── qiniu.sh └── tar-all.sh └── src ├── common.js ├── common_cn.js ├── common_en.js ├── configuration.js ├── handlers ├── menu.js ├── message.js └── update.js ├── inject ├── badge_count.js ├── chat_historys.js ├── css.js ├── emoji_parser.js ├── mention_menu.js ├── mini_frame.js ├── preload.js └── share_menu.js ├── lib └── easyIDB.js ├── main.js └── windows ├── controllers ├── app_tray.js ├── settings.js ├── splash.js └── wechat.js ├── styles ├── settings.css └── splash.css └── views ├── settings.html └── splash.html /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parser": "babel-eslint", 3 | "extends": "airbnb/base", 4 | "rules": { 5 | "strict": "off", 6 | "max-len": "off", 7 | "prefer-template": "warn", 8 | "arrow-body-style": "off", 9 | "no-unused-vars": "warn", 10 | "no-undef": "off", 11 | "array-callback-return": "off", 12 | "no-confusing-arrow": "off", 13 | "consistent-return": "warn", 14 | "no-param-reassign": "off", 15 | "default-case": "off", 16 | "guard-for-in": "off", 17 | "no-restricted-syntax": "off", 18 | "no-underscore-dangle": "off", 19 | "new-cap": "warn", 20 | "no-console": "off", 21 | "global-require": "off", 22 | "class-methods-use-this": "warn" 23 | }, 24 | } 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /dist 3 | npm-debug.log* 4 | .idea 5 | install 6 | # Dependency directories 7 | node_modules 8 | 9 | *.sublime-project 10 | *.sublime-workspace 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | sudo: required 3 | node_js: 4 | - '5.2' 5 | branches: 6 | only: 7 | - master 8 | - production 9 | before_script: 10 | - npm install 11 | script: 12 | - ./scripts/build-all.sh 13 | - ./scripts/tar-all.sh 14 | deploy: 15 | provider: releases 16 | api_key: 17 | secure: ETudcaMBembv5mq5WcA0Zu5YCQt02A8sfMIYJ+XN0dTUCFRODYgyk8SiW3ndI4zLfhsc31KbYecSVfcrvYhPlkLucdhD0hY+v4mowrGaG6q3DUE4v9+qATOE5z51MPNTQO/suPNZpeFkSCKaWh6SY9oSd/tsD+YmbcpuD0//DMiFMpYqA8ueQ7yka4SmlZq8C48MsRbULAtyHNEVNJ4en9xdE9vFHZ45kM2A2IWYVikuCa5J6YoL7N2CyIFwtKMeF68d0vwidXUXEc7z1VOHwosG7V0vEfNRrIy4mft0tXyEYe/nM8GlYnirVRCy3xF4h4ssERXbLMuZSYGm+bg/pqReL+dvsN5oKszuo7IseZnE8QfmmhfbMB4dWf8Le5WXfFgJTG28lNvl2VwTTEW4Cj5qeJmfO524GydqRE+i3uQvW4c2tBTFmfpusPnaFqVXTPH7o54hT18hYvgaBvJQv6pyMNMLLXq0BbkzquTTWTwb8lSi8XiRr/fWkQreRZNofJc21ZUSI5YcuqZpzbz1fOLseC4QJ8YXQ9b2OU/LiFF3gvHTK6vSKMQmbOFg0zFXMi5FT1SzCi/mKduax/OR/H6lolVW83eXCG1Ni+sIrwUkp0d/UL6E1pVeJMibBrOEgriWIpD+AiVzNVyBdq/oDC6qG9IXRWzii9Ks6J9zH7k= 18 | file: 19 | - 'dist/mac-osx.tar.gz' 20 | - 'dist/linux-x64.tar.gz' 21 | - 'dist/linux-ia32.tar.gz' 22 | skip_cleanup: true 23 | on: 24 | repo: geeeeeeeeek/electronic-wechat 25 | branch: production 26 | notifications: 27 | webhooks: 28 | urls: 29 | - https://webhooks.gitter.im/e/d6bab2376f47ee992d78 30 | on_success: always # options: [always|never|change] default: always 31 | on_failure: always # options: [always|never|change] default: always 32 | on_start: always # options: [always|never|change] default: always 33 | after_deploy: 34 | - ./scripts/qiniu.sh 35 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | **v2.0 (2017.02.13) CN** 4 | 5 | 1. 升级 **Electron** 至 **V1.4.15**,**Chromium** 至 **54** 6 | 2. 增加了**偏好设置**(感谢设计建议 @**[xiaoyusilen](https://github.com/xiaoyusilen)**) 7 | 3. 增加了英文版本的支持 8 | 4. 增加了一键隐藏窗口(**`ESC`** 键) 9 | 5. 修复了 **macOS** 上窗口最小化时不显示新消息提示的红点(感谢 @wujysh 的贡献) 10 | 6. 修复了聊天框内换行提示仅针对 macOS 的问题 11 | 7. 增加了两个快捷键(感谢 @awmleer 的贡献) 12 | - 搜索联系人:**`Ctrl + F`** 13 | - 切换到全屏模式:**macOS** 下 **`Ctrl + Command + F`**,**Linux** 下为 **`F11`** 14 | 8. 修复了在 Linux 系统下部分菜单按钮失效的问题(感谢 @qzchenwl 的贡献) 15 | 8. 更新了依赖的第三方库的版本至最新兼容版本 16 | 17 | **v2.0 (2017.02.13) EN** 18 | 19 | 1. Update Electron to V1.4.15, Chromium API level 54 20 | 2. Add **Preference Panel** (Thanks for the design advises from @**[xiaoyusilen](https://github.com/xiaoyusilen)**](https://github.com/xiaoyusilen)) 21 | 3. Fully support English UI! 22 | 4. Quick hide windows shortcut (**Press `ESC`**) 23 | 5. Fix **macOS** new message red dot display improperly (Thanks to @wujysh) 24 | 6. Tips in chat window now are adapted with platform 25 | 7. Add two shortcuts (Thanks to @awmleer) 26 | - Search Contact人: **`Ctrl + F`** 27 | - Toggle Fullscreen Mode: **macOS** **`Ctrl + Command + F`**, **Linux** **`F11`** 28 | 8. Fix unfunctional menu items on **Linux** (Thanks to @qzchenwl) 29 | 8. All thrid party libraries are up-to-date 30 | 31 | 32 | **v1.3 (2016.05.19)** 33 | 34 | 1. 升级 electron 至 1.1.0, Chrome 至 50.0.2661.102,Node 至 6.1.0 (感谢 @lfs1102 的贡献) 35 | 2. 新增 `brew cask` 安装方式 (最新可下载版本为 v1.2.0) 36 | 3. 新增 Windows 下的安装脚本 (感谢 @3dseals 的贡献) 37 | 4. 新增 应用启动动画,缩短首次展现时间 38 | 5. 优化 应用启动稳定性,增加超时重试 39 | 6. 优化 主要文案均统一为英文 40 | 7. 优化 减少 20M 应用体积 41 | 8. 修复 关于页面版本号显示的 bug 42 | 9. 修复 Linux 系统下左边栏组件重叠的 bug 43 | 10. 修复 部分 Linux KDE 系统下托盘图标空白的 bug 44 | 11. 其他修改 (感谢 @wzyboy, @rivershang, @hexchain, @samurai00, @boltomli 的贡献) 45 | 46 | 47 | **v1.2 (2016.04.21)** 48 | 49 | 1. 新增 更新检测模块,应用内即可检查更新 50 | 2. 新增 公众号文章的第三方分享功能。现支持一键分享到微博、QQ 空间、Facebook、Twitter、Evernote 和邮件 (感谢 @oblank 的贡献) 51 | 3. 新增 群聊 @ 提及成员功能,但收到提醒需要服务端支持 (感谢 @iamcc 的贡献) 52 | 4. 优化 登录界面使用单独的尺寸 (感谢 @xnfa 的贡献) 53 | 5. 优化 修改 OS X 下隐藏其他窗口的快捷键为 `Command+Alt+H` 54 | 6. 优化 Linux 下可执行文件文件名使用小写字母,去除空格 55 | 7. 优化 Linux 下使用彩色图标 56 | 8. 升级 `electron-prebuilt` 版本至 `0.37.6` , `electron-packager` 版本至 `7.0.0` 57 | 9. ~~降级 Emoji贴纸显示的功能。由于微信协议调整和官方代码缺陷,现有商店内贴纸及部分个人收藏的贴纸无法显示。后续跟进微信的修复进行调整。~~ (Update: 微信已修复,贴纸均可正常显示) 58 | 59 | 60 | **v1.1 (2016.03.17)** 61 | 62 | 1. 新增 OS X 和 Linux 下的托盘菜单,点击可进入应用、退出应用 (仅Linux) (感谢 @iamcc 和 @wenLiangcan 的贡献) 63 | 2. 新增 cnpm 镜像提醒 64 | 3. 优化 应用的退出逻辑,Cmd+Q 退出应用,Cmd+W 或点击关闭隐藏应用 65 | 4. 优化 OS X 和 Linux 下的应用菜单显示 66 | 5. 优化 Emoji贴纸的实现方式,避免滑动时内容抖动,无法回到底部 67 | 6. 优化 接管应用刷新的逻辑,Cmd+R 重新加载页面 68 | 7. 优化 OS X 下 build 后将应用拷贝到 Application 文件夹 69 | 8. 优化 Linux 下使用 Ctrl+Shift+I 打开开发者工具 (感谢 @wenLiangcan 的贡献) 70 | 9. 修复 错误的微信站内重定向 (感谢 @gucheen 的贡献) 71 | 10. 修复 Linux 下应用图标的显示 72 | 11. 修复 聊天列表滑动性能问题 73 | 12. 修复 公众号新窗口打开报错 (感谢 @gzzhanghao 的贡献) 74 | 75 | **v1.0 (2016.03.01)** 76 | 77 | 1. 新增 阻止消息撤回的功能 (感谢 @arrowrowe 的贡献) 78 | 2. 新增 引入了 Travis CI 和 Gitter.im 79 | 3. 优化 贴纸显示的实现方式 (感谢 @arrowrowe 的贡献) 80 | 4. 优化 build 脚本 (感谢 @gaocegege, @viko16 和 @htc550605125 的贡献) 81 | 5. 优化 Linux 下自动隐藏菜单 (感谢 @wenLiangcan 的贡献) 82 | 6. 优化 Linux 下用户头像的显示 83 | 7. 优化 禁用缩放、选中文本、默认光标 84 | 85 | **v0.1 (2016.02.19)** 86 | 87 | 1. Create the project. 88 | 2. Auto resize web content. 89 | 3. Drag to send pictures. 90 | 4. Open inhibited links without additional redirect. 91 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Electronic WeChat 2 | 3 | First of all, thanks for contributing to this project. It would be appreciated if you read through this contributing guide. 4 | 5 | ## Issues 6 | 7 | - Check if your issue is already [there](https://github.com/geeeeeeeeek/electronic-wechat/issues). 8 | 9 | - Check if your issue is `Electronic WeChat` related rather than upstream related. 10 | 11 | - Follow the guide in the issue template. 12 | 13 | ## Pull Requests 14 | 15 | PR are always welcomed. It's better if you put up an issue before firing a PR. **Remember**, the smaller your focus, the better chance to get merged. 16 | 17 | ## Be a collaborator! 18 | 19 | If you are excited about the project, and happen to have skills in Angular, Node, Electron, or else. Do not hesitate to contact me. Let's build together! 20 | -------------------------------------------------------------------------------- /CSSSAMPLE.md: -------------------------------------------------------------------------------- 1 | # 自定义CSS的一些例子 2 | 3 | 自定义的CSS会注入到里面一般来说优先级已经比较高了 4 | 如果不生效尝试加!important后缀 5 | 6 | ```css 7 | .test{ 8 | width:100px; 9 | width:100px!important; 10 | } 11 | ``` 12 | 13 | ### 1、添加聊天背景 14 | 15 | ```css 16 | .box{ 17 | background-image:url(file:///home/user/Pictures/welcome.png); 18 | background-size:60%; 19 | background-position:right; 20 | background-repeat:no-repeat; 21 | } 22 | ``` 23 | ![img](http://ww2.sinaimg.cn/large/007eZ24Wly1fx54uu32ymj30s80k7ajk) 24 | 25 | ### 2、改变气泡颜色 26 | 27 | ```css 28 | .bubble.bubble_default{ 29 | background-color: #97c9eb; 30 | } 31 | .bubble.left:after{ 32 | border-right-color: #97c9eb; 33 | } 34 | .bubble.bubble_primary{ 35 | background-color: #fcd3f3; 36 | } 37 | .bubble.bubble_primary.right:after{ 38 | border-left-color: #fcd3f3; 39 | } 40 | ``` 41 | ![img](http://ww2.sinaimg.cn/large/007eZ24Wgy1fx5klcpdq0j30ug0mmmy2) 42 | -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | #### Description 2 | 3 | First of all, thanks for your attention to open an issue for this project. 4 | Please notice that if you are requesting a **feature**, then you should give a **brief description** of your request. 5 | If you are reporting a **bug**, please **follow the template** below. 6 | A bug report **without detailed information** required will have a **very low priority** and even be **ignored** (closed directly)! 7 | 8 | #### Specifications 9 | 10 | - Version of Electron (run `$ electron --version`): `v0.0.0` 11 | - OS: `` 12 | - Stack trace from the error message (if any) 13 | 14 | ``` 15 | 16 | ``` 17 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Zhongyi Tong 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | logo 2 | 3 | # Electronic WeChat 4 | 5 | [![Gitter](https://badges.gitter.im/geeeeeeeeek/electronic-wechat.svg)](https://gitter.im/geeeeeeeeek/electronic-wechat?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge) [![Build Status](https://travis-ci.org/geeeeeeeeek/electronic-wechat.svg?branch=master)](https://travis-ci.org/geeeeeeeeek/electronic-wechat) [![Build Status](https://img.shields.io/badge/README-切换语言-yellow.svg)](README_en.md) 6 | 7 | **Mac OS X 和 Linux 下更好用的微信客户端. 更多功能, 更少bug. 使用[Electron](https://github.com/atom/electron)构建.** 8 | 9 | # 停止维护 10 | > 可能大家都发现了,网页微信正在用肉眼可见的速度砍掉各种功能,越来越多的用户包括我的两个账号也没办法登录网页微信版了,所以也没有动力继续维护了 11 | > 感谢大家这段时间的支持 12 | 13 | 其他方案 14 | 15 | - [https://github.com/Riceneeder/electronic-wechat](https://github.com/Riceneeder/electronic-wechat) 16 | - [arch用户 com.qq.weixin.deepin](https://aur.archlinux.org/packages/com.qq.weixin.deepin) 17 | 18 | 19 | ## 该分支针对使用Dock的ubuntu进行改造 20 | 21 | ### 测试环境 ubuntu 18.04 gnome 3.28.2 22 | 23 | #### 改造记录如下 24 | 25 | > 1、更新所有依赖到最新版本(180511),其中Electronic@2.0.0 26 | 27 | > ~~2、主窗体关闭按钮改为直接退出程序,不再是隐藏窗体~~ 28 | 29 | > 3、~~取消多实例检查~~,托管图标能正常使用了(偶然不能出来,感觉是Gnome的问题,关掉再开就好了。。),如果依然不行参考[#8](https://github.com/kooritea/electronic-wechat/issues/8)和[#12](https://github.com/kooritea/electronic-wechat/issues/12) 30 | 31 | > 4、修复菜单栏不能正常显示的BUG(通过ALT唤出)(无边框模式是没有菜单栏的) 32 | 33 | > ~~5、删除偏好设置中是否使用多实例的设置选项(多实例可以通过右键dock图标新建窗口实现)~~ 34 | 35 | > ~~6、检查更新选项不再调用更新脚本,而是打开浏览器到Github发布页~~ 36 | 37 | > 7、添加全局快捷键CommandOrControl+Alt+W 显示微信 38 | 39 | > 8、点击消息通知会打开微信(可以到设置页面关闭此功能) 40 | 41 | > 9、修复设置页面不知为什么addEventListener不生效导致设置页形同虚设的bug 42 | 43 | > 10、通过点击桌面的通知进入微信,现在可以直接自动打开对话框啦! 44 | 45 | > 11、优化多语言的实现方式,系统托盘的菜单汉化以及添加偏好的选项 46 | 47 | > ~~12、修复了当对方昵称或备注中有emoji时不能定位到该聊天框的bug~~(点击打开的实现方式已改变,这已经是不需要的了) 48 | 49 | > 13、炫酷无边框,鼠标移到左上角显示菜单按钮(审美设计有点菜,等一个设计师),设置页可以选择是否无边框(感谢[@waffiet-张](http://www.iconfont.cn/user/detail?spm=a313x.7781069.0.d214f71f6&uid=4435557)和[@竹尔](http://www.iconfont.cn/user/detail?spm=a313x.7781069.0.d214f71f6&uid=51853)的漂亮图标!!) 50 | 51 | > 14、托盘菜单栏加上漂亮的icon 52 | 53 | --- v2.0.5 --- 54 | 55 | > 15、更改播放视频的背景色,解决打开视频后关闭按钮看不见的问题 56 | 57 | > 16、加快了5倍左上角菜单的出现速度(0.5s → 0.1s) 58 | 59 | --- v2.0.6 --- 60 | 61 | > 17、可在偏好设置中选择关闭主窗口是否退出程序 62 | 63 | > 18、偏好设置中设置是否在程序启动的时候检查是否有可用更新 64 | 65 | --- v2.0.7 --- 66 | 67 | > 19、退出图标换回黑白色,状态栏菜单的图标也能根据选择的主题更换颜色了 68 | 69 | > 20、支持记录窗体大小 70 | 71 | --- v2.0.8 --- 72 | 73 | > 21、阻止消息撤回功能能够看到是谁撤回了消息 74 | 75 | --- v2.0.9 --- 76 | 77 | > 22、修复当主窗体处于显示状态时使用快捷键ctrl+alt+w会导致聊天窗口切换的问题 78 | 79 | --- v2.1.0 --- 80 | 81 | > 23、修复无法切换主界面语言的问题 82 | 83 | > 24、隐藏下载pc版的提示 84 | 85 | --- v2.1.1 --- 86 | 87 | > 25、修复无法点击登录和切换账号的问题 88 | 89 | --- v2.1.2 --- 90 | 91 | > 26、允许设置代理模式 92 | 93 | (1)跟随系统:使用系统的代理设置 94 | 95 | (2)直接连接:无视系统代理直接连接 96 | 97 | (3)设置代理:格式[://][:] 98 | 99 | 理论支持协议:socks5(测试通过)、socks4(未测试)、http(未测试) 100 | 101 | 例如:socks5://127.0.0.1:1080 102 | 103 | 可能有的桌面环境没有托盘图标又卡在初始化界面(卡在初始化界面一般都是网络不通)无法打开设置界面 104 | 105 | 可以直接修改~/.ew.json 106 | 107 | proxy: 108 | 109 | on:跟随系统(默认); 110 | 111 | off:直接连接; 112 | 113 | setProxy:设置代理地址 114 | 115 | proxy-url:代理地址 116 | 117 | --- v2.1.3 --- 118 | 119 | >27、可自由改变文字输入区域的大小 120 | 121 | --- v2.1.4 --- 122 | 123 | >28、历史消息记录,主要参考了 @iamcc 的[思路](https://github.com/geeeeeeeeek/electronic-wechat/pull/159/commits/9abbe6e177d4f02aae6529136e4d48b3ef6a2c36) 124 | 125 | >29、艾特的时候显示群名片,PS:这个是暴力艾特,对方并不会收到提醒(手动笑哭) 126 | 127 | >30、不再解析表情商店的表情(直接显示文本),因为微信已经关闭了这个接口了 128 | 129 | --- v2.1.5 --- 130 | 131 | >31、更换点击通知打开微信的实现方式(这次应该不会缺消息了) 132 | 133 | >32、稍微改了一下设置页的样式 134 | 135 | >33、优化微信休眠机制(微信被隐藏的时候不会把新消息设为已读) 136 | 137 | --- v2.1.6 --- 138 | 139 | >34、修复右键复制无效问题 ~~(仅限文字消息,不包括链接)~~ 2.1.9已经可以复制链接了 140 | 141 | >35、可以正确地使用是否允许多实例运行 142 | 143 | --- v2.1.7 --- 144 | 145 | >36、可设置失焦时是否进入挂起状态 146 | 147 | --- v2.1.8 --- 148 | 149 | >37、修复无法刷新二维码的问题 150 | 151 | >38、修复文件助手无法保存记录问题 152 | 153 | >39、历史记录显示包括日期的详细时间 154 | 155 | --- v2.1.9 --- 156 | 157 | >40、electron更新到2.0.8 修复若干bug 158 | 159 | >41、修复若干地方报错 160 | 161 | >42、修复链接无法复制问题 162 | 163 | >43、修复视频无法暂停问题 164 | 165 | --- v2.2.0 --- 166 | 167 | >44、修复加载全部历史纪录后提示错误问题 168 | 169 | >45、优化历史记录获取体验 170 | 171 | >46、聚焦时自动切换到最后一次聊天窗口 172 | 173 | >47、修复手机上发送的私聊消息保存错误问题 174 | 175 | --- v2.2.1 --- 176 | 177 | >48、程序退出时自动退出网页版登录 178 | 179 | >49、登陆后保持窗体位置 180 | 181 | >50、隐藏通知内容(默认关闭) 182 | 183 | >51、electron 3.0.6 184 | 185 | >52、修复无法下载历史记录中的文件问题 186 | 187 | --- v2.3.0 --- 188 | 189 | >53、自定义CSS [样例](https://github.com/kooritea/electronic-wechat/blob/master/CSSSAMPLE.md) 190 | 191 | >54、支持使用ctrl+滑轮 缩放调整 192 | 193 | >55、electron 3.0.8 194 | 195 | >56、当有文本被选中时复制会只复制选中的文本 196 | 197 | >58、可在设置中清除聊天记录和选择是否保存聊天记录 198 | 199 | >59、自由选择在桌面通知上显示的内容 200 | 201 | --- v2.3.1 --- 202 | 203 | >60、跟随官方更改消息对象结构 204 | 205 | >61、electron 4.0.5 206 | 207 |
208 |
209 |
210 | 211 | #### 已知BUG 212 | 213 | > ~~提示更新的弹框不能显示微信图标~~(原来是electron的锅 wechat2.2.1 electron3.0.6 已经没有这个问题) 214 | 215 | > 历史消息无法右键 216 | 217 | > 在允许多实例的情况下,当打开第二个实例的时候会自动登录前一个账号,需要手动退出(有需要的话最好就先打开两个实例再登录) 218 | 219 |
220 |
221 |
222 | 223 | #### todo 224 | 225 | > 快速艾特 226 | 227 | > 临时关闭群通知 228 | 229 | > snap 230 | 231 | ~~虽然想做保留本地聊天记录,可是我不会angular(摊手),给各位前端丢脸了!~~ 232 | 233 | 虽然做了保存历史记录,可我还不会angular 234 | 235 | #### [下载构建好的应用](https://github.com/kooritea/electronic-wechat/releases) 236 | 237 | ### 创建快捷方式 238 | 修改electronic-wechat.desktop中的路径 239 | 复制到/usr/share/applications/或者~/.local/share/applications/ 240 | 即可在应用程序中找到electronic-wechat的图标 241 | 242 | 或者参考[issues19](https://github.com/kooritea/electronic-wechat/issues/19) 243 | 244 | 245 | # 以下是原仓库的Readme 246 | 247 | 248 | 249 | **Important:** 如果你希望在自己的电脑上构建 Electronic WeChat,请使用 [production branch](https://github.com/geeeeeeeeek/electronic-wechat/tree/production),master branch 包含正在开发的部分,并且不能保证是稳定的版本——尽管 production 版本也有bug :D 250 | 251 | ![qq20160428-0 2x](https://cloud.githubusercontent.com/assets/7262715/14876747/ff691ade-0d49-11e6-8435-cb1fac91b3c2.png) 252 | 253 | ## 应用特性 ([更新日志](CHANGELOG.md)) 254 | 255 | - **来自网页版微信的更现代的界面和更丰富的功能** 256 | - **阻止消息撤回** 257 | - **显示表情贴纸** [[?]](https://github.com/geeeeeeeeek/electronic-wechat/issues/2) 258 | - 公众号文章支持一键分享到微博、QQ 空间、Facebook、Twitter、Evernote 和邮件 259 | - 拖入图片、文件即可发送 260 | - 群聊 @ 提及成员 261 | - 原生应用体验,未读消息小红点、消息通知等数十项优化 262 | - 去除外链重定向,直接打开淘宝等网站 263 | - 没有原生客户端万年不修复的bug 264 | 265 | ## 如何使用 266 | 267 | 在下载和运行这个项目之前,你需要在电脑上安装 [Git](https://git-scm.com) 和 [Node.js](https://nodejs.org/en/download/) (来自 [npm](https://www.npmjs.com/))。在命令行中输入: 268 | 269 | ``` bash 270 | # 下载仓库 271 | git clone https://github.com/geeeeeeeeek/electronic-wechat.git 272 | # 进入仓库 273 | cd electronic-wechat 274 | # 安装依赖, 运行应用 275 | npm install && npm start 276 | ``` 277 | 278 | 根据你的平台打包应用: 279 | 280 | ``` shell 281 | npm run build:osx 282 | npm run build:linux 283 | npm run build:win 284 | ``` 285 | 286 | **提示:** 如果 `npm install` 下载缓慢,你可以使用 [淘宝镜像(cnpm)](http://npm.taobao.org/) 替代 npm 。 287 | 288 | **新渠道:** 使用你熟悉的包管理工具安装。请查看 [社区贡献的镜像](https://github.com/geeeeeeeeek/electronic-wechat/wiki/System-Support-Matrix#%E7%A4%BE%E5%8C%BA%E8%B4%A1%E7%8C%AE%E7%9A%84%E5%AE%89%E8%A3%85%E5%8C%85) 。 289 | 290 | **新渠道:** homebrew 安装也已支持 (更新至 electronic-wechat v1.2.0)! 291 | 292 | ```bash 293 | brew cask install electronic-wechat 294 | ``` 295 | 296 | #### [下载开箱即用的稳定版应用](https://github.com/geeeeeeeeek/electronic-wechat/releases) 297 | 298 | #### 项目使用 [MIT](LICENSE.md) 许可 299 | 300 | *Electronic WeChat* 是这个开源项目发布的产品。网页版微信是其中重要的一部分,但请注意这是一个社区发布的产品,而 *不是* 官方微信团队发布的产品。 301 | -------------------------------------------------------------------------------- /README_en.md: -------------------------------------------------------------------------------- 1 | logo 2 | 3 | # Electronic WeChat 4 | 5 | *A better WeChat on macOS and Linux. Built with [Electron](https://github.com/atom/electron).* 6 | 7 | ## This branch is specially designed for ubuntu. 8 | 9 | **Important:** If you want to build the app by yourself rather than download the release directly, please consider to use the source code from [the production branch](https://github.com/geeeeeeeeek/electronic-wechat/tree/production), the master branch is under development and we cannot guarantee it to be stable. 10 | 11 | [![Gitter](https://badges.gitter.im/geeeeeeeeek/electronic-wechat.svg)](https://gitter.im/geeeeeeeeek/electronic-wechat?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=body_badge) 12 | [![Build Status](https://travis-ci.org/geeeeeeeeek/electronic-wechat.svg?branch=master)](https://travis-ci.org/geeeeeeeeek/electronic-wechat) 13 | [![Build Status](https://img.shields.io/github/stars/geeeeeeeeek/electronic-wechat.svg)](https://github.com/geeeeeeeeek/electronic-wechat) 14 | [![Build Status](https://img.shields.io/github/forks/geeeeeeeeek/electronic-wechat.svg)](https://github.com/geeeeeeeeek/electronic-wechat) 15 | [![Build Status](https://img.shields.io/badge/README-切换语言-yellow.svg)](README.md) 16 | 17 | ![qq20160428-0 2x](https://cloud.githubusercontent.com/assets/7262715/14876747/ff691ade-0d49-11e6-8435-cb1fac91b3c2.png) 18 | 19 | ## Features ([CHANGELOG](CHANGELOG.md)) 20 | 21 | - **Modern UI and all features from Web WeChat.** 22 | - **Block message recall.** 23 | - **Stickers showing support.** [[?]](https://github.com/geeeeeeeeek/electronic-wechat/issues/2) 24 | - Share subscribed passages on Weibo, Qzone, Facebook, Twitter, Evernote, and email. 25 | - Mention users in a group chat. 26 | - Drag and drop to send photos. 27 | - Behaves like a native app, based on dozens of optimization. 28 | - Removes URL link redirects and takes you directly to blocked websites (e.g. taobao.com). 29 | 30 | ## How To Use 31 | 32 | To clone and run this repository you'll need [Git](https://git-scm.com) and [Node.js](https://nodejs.org/en/download/) (which comes with [npm](https://www.npmjs.com/)) installed on your computer. From your command line: 33 | 34 | ``` bash 35 | # Clone this repository 36 | git clone https://github.com/geeeeeeeeek/electronic-wechat.git 37 | # Go into the repository 38 | cd electronic-wechat 39 | # Install dependencies and run the app 40 | npm install && npm start 41 | ``` 42 | 43 | To pack into an app, simply type one of these: 44 | 45 | ``` shell 46 | npm run build:osx 47 | npm run build:linux 48 | npm run build:win32 49 | npm run build:win64 50 | ``` 51 | 52 | **New:** Install with your familiar package manager. Check out [images maintained by the community](https://github.com/geeeeeeeeek/electronic-wechat/wiki/System-Support-Matrix#%E7%A4%BE%E5%8C%BA%E8%B4%A1%E7%8C%AE%E7%9A%84%E5%AE%89%E8%A3%85%E5%8C%85)! 53 | 54 | **New:** Or, with homebrew! 55 | 56 | ```bash 57 | brew cask install electronic-wechat 58 | ``` 59 | 60 | #### [Download Released App](https://github.com/geeeeeeeeek/electronic-wechat/releases) 61 | 62 | #### License [MIT](LICENSE.md) 63 | 64 | *Electronic WeChat* is released by this open source project. While Web WeChat is a major component in the app, it should be noted that this is a community release and not an official WeChat release. 65 | -------------------------------------------------------------------------------- /assets/256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kooritea/electronic-wechat/88a4d10e0e1e9572b86b9e00c2d35be7abdf65c4/assets/256x256.png -------------------------------------------------------------------------------- /assets/close_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kooritea/electronic-wechat/88a4d10e0e1e9572b86b9e00c2d35be7abdf65c4/assets/close_white.png -------------------------------------------------------------------------------- /assets/exit_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kooritea/electronic-wechat/88a4d10e0e1e9572b86b9e00c2d35be7abdf65c4/assets/exit_black.png -------------------------------------------------------------------------------- /assets/exit_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kooritea/electronic-wechat/88a4d10e0e1e9572b86b9e00c2d35be7abdf65c4/assets/exit_white.png -------------------------------------------------------------------------------- /assets/fullscreen-exit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kooritea/electronic-wechat/88a4d10e0e1e9572b86b9e00c2d35be7abdf65c4/assets/fullscreen-exit.png -------------------------------------------------------------------------------- /assets/fullscreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kooritea/electronic-wechat/88a4d10e0e1e9572b86b9e00c2d35be7abdf65c4/assets/fullscreen.png -------------------------------------------------------------------------------- /assets/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kooritea/electronic-wechat/88a4d10e0e1e9572b86b9e00c2d35be7abdf65c4/assets/icon.icns -------------------------------------------------------------------------------- /assets/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kooritea/electronic-wechat/88a4d10e0e1e9572b86b9e00c2d35be7abdf65c4/assets/icon.png -------------------------------------------------------------------------------- /assets/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kooritea/electronic-wechat/88a4d10e0e1e9572b86b9e00c2d35be7abdf65c4/assets/loading.gif -------------------------------------------------------------------------------- /assets/settings_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kooritea/electronic-wechat/88a4d10e0e1e9572b86b9e00c2d35be7abdf65c4/assets/settings_black.png -------------------------------------------------------------------------------- /assets/settings_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kooritea/electronic-wechat/88a4d10e0e1e9572b86b9e00c2d35be7abdf65c4/assets/settings_white.png -------------------------------------------------------------------------------- /assets/status_bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kooritea/electronic-wechat/88a4d10e0e1e9572b86b9e00c2d35be7abdf65c4/assets/status_bar.png -------------------------------------------------------------------------------- /assets/status_bar@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kooritea/electronic-wechat/88a4d10e0e1e9572b86b9e00c2d35be7abdf65c4/assets/status_bar@2x.png -------------------------------------------------------------------------------- /assets/tray_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kooritea/electronic-wechat/88a4d10e0e1e9572b86b9e00c2d35be7abdf65c4/assets/tray_black.png -------------------------------------------------------------------------------- /assets/tray_exit_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kooritea/electronic-wechat/88a4d10e0e1e9572b86b9e00c2d35be7abdf65c4/assets/tray_exit_black.png -------------------------------------------------------------------------------- /assets/tray_exit_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kooritea/electronic-wechat/88a4d10e0e1e9572b86b9e00c2d35be7abdf65c4/assets/tray_exit_white.png -------------------------------------------------------------------------------- /assets/tray_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kooritea/electronic-wechat/88a4d10e0e1e9572b86b9e00c2d35be7abdf65c4/assets/tray_icon.png -------------------------------------------------------------------------------- /assets/tray_settings_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kooritea/electronic-wechat/88a4d10e0e1e9572b86b9e00c2d35be7abdf65c4/assets/tray_settings_black.png -------------------------------------------------------------------------------- /assets/tray_settings_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kooritea/electronic-wechat/88a4d10e0e1e9572b86b9e00c2d35be7abdf65c4/assets/tray_settings_white.png -------------------------------------------------------------------------------- /assets/tray_unread_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kooritea/electronic-wechat/88a4d10e0e1e9572b86b9e00c2d35be7abdf65c4/assets/tray_unread_black.png -------------------------------------------------------------------------------- /assets/tray_unread_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kooritea/electronic-wechat/88a4d10e0e1e9572b86b9e00c2d35be7abdf65c4/assets/tray_unread_white.png -------------------------------------------------------------------------------- /assets/tray_white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kooritea/electronic-wechat/88a4d10e0e1e9572b86b9e00c2d35be7abdf65c4/assets/tray_white.png -------------------------------------------------------------------------------- /assets/vertical-align-botto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kooritea/electronic-wechat/88a4d10e0e1e9572b86b9e00c2d35be7abdf65c4/assets/vertical-align-botto.png -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "osx" : { 3 | "title": "Electronic Wechat", 4 | "background": "icon.png", 5 | "icon": "icon.icns", 6 | "icon-size": 80, 7 | "contents": [ 8 | { "x": 438, "y": 344, "type": "link", "path": "/Applications" }, 9 | { "x": 192, "y": 344, "type": "file" } 10 | ] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /electronic-wechat.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Electronic Wechat 3 | Comment=Unofficial WeChat client built with React, MobX and Electron. 4 | Exec=/opt/electronic-wechat-linux-x64/electronic-wechat %U 5 | Terminal=false 6 | Type=Application 7 | Icon=/opt/electronic-wechat-linux-x64/assets/icon.png 8 | Categories=Network;Utility;Chat; 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "electronic-wechat", 3 | "version": "2.3.1", 4 | "description": "An Electron application for WeChat", 5 | "main": "src/main.js", 6 | "scripts": { 7 | "start": "electron src/main.js", 8 | "build": "./scripts/build-all.sh", 9 | "build:osx": "./scripts/build.sh darwin x64", 10 | "build:osx64": "./scripts/build.sh darwin x64", 11 | "build:linux32": "./scripts/build.sh linux ia32", 12 | "build:linux": "./scripts/build.sh linux x64", 13 | "build:linux64": "./scripts/build.sh linux x64", 14 | "build:win": ".\\scripts\\build-win32.bat win32 ia32", 15 | "build:win32": ".\\scripts\\build-win32.bat win32 ia32", 16 | "build:win64": ".\\scripts\\build-win32.bat win32 x64" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/kooritea/wechat-electron.git", 21 | "originalurl": "https://github.com/geeeeeeeeek/wechat-electron.git" 22 | }, 23 | "keywords": [ 24 | "Electron", 25 | "WeChat", 26 | "微信", 27 | "Web" 28 | ], 29 | "author": { 30 | "name": "kooritea", 31 | "originalauthor": "Zhongyi Tong", 32 | "email": "e@gmail.com" 33 | }, 34 | "license": "MIT", 35 | "bugs": { 36 | "url": "https://github.com/kooritea/wechat-electron/issues", 37 | "originalurl": "https://github.com/geeeeeeeeek/wechat-electron/issues" 38 | }, 39 | "homepage": "https://github.com/kooritea/wechat-electron/", 40 | "originalhomepage": "https://github.com/geeeeeeeeek/wechat-electron/", 41 | "dependencies": { 42 | "electron-localshortcut": "^3.1.0", 43 | "electron-packager": "^12.1.1", 44 | "emojione": "^3.1.4", 45 | "is-xfce": "^2.0.0", 46 | "nconf": "^0.10.0", 47 | "pinyin": "^2.8.0" 48 | }, 49 | "devDependencies": { 50 | "babel-eslint": "^8.2.3", 51 | "electron": "4.0.5", 52 | "eslint": "^4.19.1", 53 | "eslint-config-airbnb": "^16.1.0", 54 | "eslint-plugin-import": "^2.2.0", 55 | "eslint-plugin-jsx-a11y": "^6.0.3", 56 | "eslint-plugin-react": "^7.7.0" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /scripts/build-all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if ! hash electron-packager 2>/dev/null; then 4 | RED='\033[0;31m' 5 | NC='\033[0m' 6 | echo "${RED}Error${NC}: you need to npm install electron-packager. Aborting." 7 | exit 1 8 | fi 9 | 10 | function build() { 11 | ./scripts/build.sh $@ 12 | } 13 | 14 | build darwin x64 15 | build linux ia32 16 | build linux x64 17 | #build win32 ia32 18 | -------------------------------------------------------------------------------- /scripts/build-win32.bat: -------------------------------------------------------------------------------- 1 | set PLATFORM=%1% 2 | set ARCH=%2% 3 | set APP_NAME="Electronic WeChat" 4 | 5 | set ignore_list="dist|scripts|\.idea|.*\.md|.*\.yml|node_modules/nodejieba" 6 | 7 | electron-packager . "%APP_NAME%" --platform=%PLATFORM% --arch=%ARCH% --electronVersion=1.4.15 --app-version=1.4.0 --asar --icon=assets\icon.png --overwrite --out=.\dist --ignore=%ignore_list% 8 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | if ! hash electron-packager 2>/dev/null; then 4 | RED='\033[0;31m' 5 | NC='\033[0m' 6 | echo "${RED}Error${NC}: you need to npm install electron-packager. Aborting." 7 | exit 1 8 | fi 9 | 10 | if [ "$#" -ne 2 ]; then 11 | echo -e "Usage: ./script/build.sh " 12 | echo -e " platform: darwin, linux, win32" 13 | echo -e " arch: ia32, x64" 14 | exit 1 15 | fi 16 | 17 | PLATFORM=$1 18 | ARCH=$2 19 | 20 | echo "Start packaging for $PLATFORM $ARCH." 21 | 22 | if [ $PLATFORM = "linux" ]; then 23 | APP_NAME="electronic-wechat" 24 | else 25 | APP_NAME="Electronic WeChat" 26 | fi 27 | 28 | ignore_list="dist|scripts|\.idea|.*\.md|.*\.yml|node_modules/nodejieba|install" 29 | 30 | electron-packager . "${APP_NAME}" --platform=$PLATFORM --arch=$ARCH --electronVersion=4.0.5 --app-version=2.3.1 --asar --icon=assets/icon.icns --overwrite --out=./dist --ignore=${ignore_list} 31 | 32 | if [ $? -eq 0 ]; then 33 | echo -e "$(tput setaf 2)Packaging for $PLATFORM $ARCH succeeded.$(tput sgr0)\n" 34 | fi 35 | 36 | if [ $PLATFORM = "darwin" ]; then 37 | ditto -rsrcFork ./dist/Electronic\ WeChat-darwin-x64/Electronic\ WeChat.app /Applications/Electronic\ WeChat.app 38 | echo "$(tput setaf 3)App copied to /Applications. You can open Electronic WeChat there or from Spotlight.$(tput sgr0)" 39 | fi 40 | 41 | cp ./electronic-wechat.desktop ./dist/electronic-wechat-linux-x64/ 42 | mkdir ./dist/electronic-wechat-linux-x64/assets 43 | cp ./assets/icon.png ./dist/electronic-wechat-linux-x64/assets/icon.png 44 | -------------------------------------------------------------------------------- /scripts/qiniu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | case "$(uname -s)" in 4 | 5 | Linux*) 6 | wget http://devtools.qiniu.com/qiniu-devtools-linux_amd64-current.tar.gz -O dist/qiniu-devtools.tar.gz 7 | ;; 8 | 9 | Darwin) 10 | wget http://devtools.qiniu.io/qiniu-devtools-darwin_amd64-current.tar.gz -O dist/qiniu-devtools.tar.gz 11 | ;; 12 | 13 | *) 14 | ;; 15 | 16 | esac 17 | 18 | mkdir dist/qiniu-devtools 19 | tar -xvf dist/qiniu-devtools.tar.gz -C dist/qiniu-devtools 20 | rm -rf /tmp/qiniu 21 | mkdir /tmp/qiniu 22 | cp dist/mac-osx.tar.gz /tmp/qiniu 23 | cp dist/linux-x64.tar.gz /tmp/qiniu 24 | cp dist/linux-ia32.tar.gz /tmp/qiniu 25 | 26 | echo '{ 27 | "src": "/tmp/qiniu", 28 | "dest": "qiniu:access_key='$QINIU_ACCESS_KEY'&secret_key='$QINIU_SECRET_KEY'&bucket=flymeos-cancro", 29 | "debug_level": 1 30 | }' > qiniu-config.json 31 | ./dist/qiniu-devtools/qrsync qiniu-config.json 32 | -------------------------------------------------------------------------------- /scripts/tar-all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd dist 4 | 5 | echo 'Start compressing for Mac OS X.' 6 | tar zcf 'mac-osx.tar.gz' 'Electronic WeChat-darwin-x64' 7 | echo 'Compressing for Mac OS X succeed.' 8 | 9 | echo 'Start compressing for Linux x64.' 10 | tar zcf 'linux-x64.tar.gz' 'electronic-wechat-linux-x64' 11 | echo 'Compressing for Linux x64 succeed.' 12 | 13 | echo 'Start compressing for Linux ia32.' 14 | tar zcf 'linux-ia32.tar.gz' 'electronic-wechat-linux-ia32' 15 | echo 'Compressing for Linux ia32 succeed.' 16 | 17 | cd .. 18 | -------------------------------------------------------------------------------- /src/common.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const AppConfig = require('./configuration'); 4 | 5 | const lan = AppConfig.readSettings('language'); 6 | 7 | let Common; 8 | if (lan === 'zh-CN') { 9 | Common = require('./common_cn'); 10 | } else { 11 | Common = require('./common_en'); 12 | } 13 | 14 | module.exports = Common; 15 | -------------------------------------------------------------------------------- /src/common_cn.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Zhongyi on 3/26/16. 3 | */ 4 | 'use strict'; 5 | 6 | class Common { 7 | 8 | } 9 | Common.ELECTRON = 'Electron'; 10 | Common.ELECTRONIC_WECHAT = 'Electronic WeChat'; 11 | Common.ELECTRONIC_SETINGS = '偏好设置'; 12 | Common.DEBUG_MODE = false; 13 | Common.WINDOW_SIZE = { 14 | width: 800, 15 | height: 600, 16 | }; 17 | Common.WINDOW_SIZE_LOGIN = { 18 | width: 380, 19 | height: 540, 20 | }; 21 | Common.WINDOW_SIZE_LOADING = { 22 | width: 380, 23 | height: 120, 24 | }; 25 | Common.WINDOW_SIZE_SETTINGS = { 26 | width: 800, 27 | height: 600, 28 | }; 29 | 30 | Common.USER_AGENT = { 31 | 'freebsd': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36', 32 | 'sunos': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36', 33 | 'win32': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36', 34 | 'linux': 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36', 35 | 'darwin': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36' 36 | } 37 | 38 | Common.WEB_WECHAT = 'https://wx2.qq.com/?lang=zh_CN'; 39 | Common.GITHUB = 'https://github.com/geeeeeeeeek/electronic-wechat'; 40 | Common.FORKER_GITHUB = 'https://github.com/kooritea/electronic-wechat' 41 | Common.GITHUB_RELEASES = 'https://github.com/geeeeeeeeek/electronic-wechat/releases'; 42 | Common.FORKER_GITHUB_RELEASES = 'https://github.com/kooritea/electronic-wechat/releases'; 43 | Common.GITHUB_ISSUES = 'https://github.com/geeeeeeeeek/electronic-wechat/issues'; 44 | Common.FORKER_GITHUB_ISSUES = 'https://github.com/kooritea/electronic-wechat/issues' 45 | Common.GITHUB_API_HOST = 'api.github.com'; 46 | Common.GITHUB_API_RELEASE_LATEST_PATH = '/repos/geeeeeeeeek/electronic-wechat/releases/latest'; 47 | Common.FORKER_GITHUB_API_RELEASE_LATEST_PATH = '/repos/kooritea/electronic-wechat/releases/latest'; 48 | 49 | Common.UPDATE_ERROR_ELECTRON = 'Failed to get the local version. If you are using debug mode(by `npm start`), this error would happen. Use packed app instead or manually check for updates.\n\n' + Common.GITHUB_RELEASES; 50 | Common.UPDATE_ERROR_EMPTY_RESPONSE = '没能获取最新的更新信息'; 51 | Common.UPDATE_ERROR_UNKNOWN = '不造什么出错了...'; 52 | Common.UPDATE_NA_TITLE = '没有可用的更新'; 53 | Common.UPDATE_ERROR_NETWORK = '网络连接出错,请检查你的网络'; 54 | Common.UPDATE_ERROR_LATEST = (version) => { 55 | return `已经在使用最新版 - (${version})`; 56 | }; 57 | 58 | Common.MENTION_MENU_INITIAL_X = 300; 59 | Common.MENTION_MENU_OFFSET_X = 30; 60 | Common.MENTION_MENU_INITIAL_Y = 140; 61 | Common.MENTION_MENU_OFFSET_Y = 45; 62 | Common.MENTION_MENU_WIDTH = 120; 63 | Common.MENTION_MENU_OPTION_HEIGHT = 30; 64 | Common.MENTION_MENU_OPTION_DEFAULT_NUM = 4; 65 | Common.MENTION_MENU_HINT_TEXT = '选择回复的人:'; 66 | Common.TEAM_MESSAGE = '群聊消息' 67 | Common.WECHAT_MESSAGE = '微信消息' 68 | Common.RECEIVED_TEAM_MESSAGE = '收到一条群聊消息' 69 | 70 | Common.MESSAGE_PREVENT_RECALL = (name) => { 71 | return `阻止了 \"${name}\" 的一次撤回` 72 | } 73 | Common.EMOJI_MAXIUM_SIZE = 120; 74 | 75 | Common.clearHistoryConfirm = '确定要清除所有聊天记录?' 76 | 77 | Common.MENU = { 78 | about: '关于 Electronic Wechat', 79 | service: '服务', 80 | hide: '隐藏应用', 81 | hideOther: '隐藏其他窗口', 82 | showAll: '显示全部窗口', 83 | pref: '偏好', 84 | quit: '退出', 85 | edit: '编辑', 86 | undo: '撤销', 87 | redo: '取消撤销', 88 | cut: '剪切', 89 | copy: '复制', 90 | paste: '粘贴', 91 | selectAll: '选择全部', 92 | view: '视图', 93 | reload: '重新加载当前窗口', 94 | toggleFullScreen:'切换全屏', 95 | searchContacts:'搜索联系人', 96 | devtool: '开发者工具', 97 | window: '窗口', 98 | min: '最小化', 99 | close: '关闭', 100 | allFront: '全部打开', 101 | help: '帮助', 102 | repo: 'GitHub 目录', 103 | repo_fork: '该分支 Github 目录', 104 | feedback: '联系我们', 105 | feedback_forker: '联系该分支开发者', 106 | checkRelease: '检查更新', 107 | }; 108 | 109 | Common.TRAY = { 110 | show:'显示微信', 111 | pref:'偏好', 112 | exit:'退出' 113 | } 114 | 115 | 116 | module.exports = Common; 117 | -------------------------------------------------------------------------------- /src/common_en.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Zhongyi on 3/26/16. 3 | */ 4 | 'use strict'; 5 | 6 | class Common { 7 | 8 | } 9 | Common.ELECTRON = 'Electron'; 10 | Common.ELECTRONIC_WECHAT = 'Electronic WeChat'; 11 | Common.ELECTRONIC_SETINGS = 'Setings'; 12 | Common.DEBUG_MODE = false; 13 | Common.WINDOW_SIZE = { 14 | width: 800, 15 | height: 600, 16 | }; 17 | Common.WINDOW_SIZE_LOGIN = { 18 | width: 380, 19 | height: 540, 20 | }; 21 | Common.WINDOW_SIZE_LOADING = { 22 | width: 380, 23 | height: 120, 24 | }; 25 | Common.WINDOW_SIZE_SETTINGS = { 26 | width: 800, 27 | height: 600, 28 | }; 29 | 30 | Common.USER_AGENT = { 31 | freebsd: 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36', 32 | sunos: 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36', 33 | win32: 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36', 34 | linux: 'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36', 35 | darwin: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.1 Safari/537.36', 36 | }; 37 | 38 | Common.WEB_WECHAT = 'https://wx2.qq.com/?lang=en_US'; 39 | Common.GITHUB = 'https://github.com/geeeeeeeeek/electronic-wechat'; 40 | Common.FORKER_GITHUB = 'https://github.com/kooritea/electronic-wechat' 41 | Common.GITHUB_RELEASES = 'https://github.com/geeeeeeeeek/electronic-wechat/releases'; 42 | Common.FORKER_GITHUB_RELEASES = 'https://github.com/kooritea/electronic-wechat/releases'; 43 | Common.GITHUB_ISSUES = 'https://github.com/geeeeeeeeek/electronic-wechat/issues'; 44 | Common.FORKER_GITHUB_ISSUES = 'https://github.com/kooritea/electronic-wechat/issues' 45 | Common.GITHUB_API_HOST = 'api.github.com'; 46 | Common.GITHUB_API_RELEASE_LATEST_PATH = '/repos/geeeeeeeeek/electronic-wechat/releases/latest'; 47 | 48 | Common.UPDATE_ERROR_ELECTRON = `Failed to get the local version. If you are using debug mode(by \`npm start\`), this error would happen. Use packed app instead or manually check for updates.\n\n${Common.GITHUB_RELEASES}`; 49 | Common.UPDATE_ERROR_EMPTY_RESPONSE = 'Failed to fetch release info.'; 50 | Common.UPDATE_ERROR_UNKNOWN = 'Something went wrong.'; 51 | Common.UPDATE_NA_TITLE = 'No Update Available'; 52 | Common.UPDATE_ERROR_NETWORK = 'Connection hang up unexpectedly. Check your network settings.'; 53 | Common.UPDATE_ERROR_LATEST = (version) => { 54 | return `You are using the latest version(${version}).`; 55 | }; 56 | 57 | Common.MENTION_MENU_INITIAL_X = 300; 58 | Common.MENTION_MENU_OFFSET_X = 30; 59 | Common.MENTION_MENU_INITIAL_Y = 140; 60 | Common.MENTION_MENU_OFFSET_Y = 45; 61 | Common.MENTION_MENU_WIDTH = 120; 62 | Common.MENTION_MENU_OPTION_HEIGHT = 30; 63 | Common.MENTION_MENU_OPTION_DEFAULT_NUM = 4; 64 | Common.MENTION_MENU_HINT_TEXT = 'Mention:'; 65 | Common.TEAM_MESSAGE = 'Team message'; 66 | Common.WECHAT_MESSAGE = 'Wechat message' 67 | Common.RECEIVED_TEAM_MESSAGE = 'Received a team Wechat message' 68 | 69 | Common.MESSAGE_PREVENT_RECALL = (name) => { 70 | return `Blocked ${name} a message recall.` 71 | } 72 | Common.EMOJI_MAXIUM_SIZE = 120; 73 | 74 | Common.languageTitle = 'Language(Need to Restart)'; 75 | Common.languageDesc = 'Select a default language for WeChat!'; 76 | Common.recallTitle = 'Prevent Message Recall'; 77 | Common.recallDesc = 'Message recall feature might be annoying'; 78 | Common.notificationTitle = 'Open WeChat from the notification'; 79 | Common.notificationDesc = 'Select whether you can open WeChat by clicking on the message notification' 80 | Common.frameTitle = 'No border(Need to Restart)'; 81 | Common.frameDesc = 'Select whether to hide the border' 82 | Common.closeTitle = 'Close the main panel'; 83 | Common.closeDesc = 'Choose to close the main panel to minimize the system tray or exit the program'; 84 | Common.updateTitle = 'Automatic check update'; 85 | Common.updateDesc = 'Whether the update is automatically checked when the program is opened'; 86 | Common.instanceTitle = 'Allow Multiple Instance'; 87 | Common.instanceDesc = 'Multiple instance can login with different accounts'; 88 | Common.iconTitle = 'File Path (In Development)'; 89 | Common.iconDesc = 'Set a default file path'; 90 | Common.trayTitle = 'Tray Icon color (Black/White)'; 91 | Common.trayDesc = 'Select a color to match your desktop theme'; 92 | Common.proxyTitle = 'Set Proxy(Need to Restart)' 93 | Common.proxyDesc = 'Select Proxy Mode' 94 | Common.cssTitle = 'The custom CSS' 95 | Common.cssDesc = 'Customize the style of the wechat' 96 | Common.zoomTitle = 'set scaling' 97 | Common.zoomDesc = 'use ctrl+mouse wheel on wechat window' 98 | Common.blurTitle = 'Blur Processing' 99 | Common.blurDesc = 'Whether to go into suspended state when blur' 100 | Common.selectNotificationBodyTitle = 'notification' 101 | Common.selectNotificationBodyDesc = 'filter notification info' 102 | Common.historyTitle = 'Chat History' 103 | Common.historyDesc = 'Keep Chat History(Need to Restart)' 104 | Common.clearHistoryConfirm = 'Make sure to clear all chat history' 105 | 106 | Common.UPGRADE = 'UPGRADE'; 107 | Common.FEEDBACK = 'FEEDBACK'; 108 | 109 | Common.MENU = { 110 | about: 'About Electronic Wechat', 111 | service: 'Service', 112 | hide: 'Hide Application', 113 | hideOther: 'Hide Others', 114 | showAll: 'Show All', 115 | pref: 'Preference', 116 | quit: 'Quit', 117 | edit: 'Edit', 118 | undo: 'Undo', 119 | redo: 'Redo', 120 | cut: 'Cut', 121 | copy: 'Copy', 122 | paste: 'Paste', 123 | selectAll: 'Select All', 124 | view: 'View', 125 | reload: 'Reload This Window', 126 | toggleFullScreen: 'Toggle Full Screen', 127 | searchContacts: 'Search Contacts', 128 | devtool: 'Toggle DevTools', 129 | window: 'Window', 130 | min: 'Minimize', 131 | close: 'Close', 132 | allFront: 'Bring All to Front', 133 | help: 'Help', 134 | repo: 'GitHub Repository', 135 | repo_fork: 'The Branch GitHub Repository', 136 | feedback: 'Report Issue', 137 | feedback_forker: 'Report Issue To This Branch', 138 | checkRelease: 'Check for New Release', 139 | }; 140 | 141 | Common.TRAY = { 142 | show:'show', 143 | pref: 'Preference', 144 | exit:'exit' 145 | } 146 | 147 | module.exports = Common; 148 | -------------------------------------------------------------------------------- /src/configuration.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function getUserHome() { 4 | return process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME']; 5 | } 6 | 7 | const nconf = require('nconf').file({ 8 | file: `${getUserHome()}/.ew.json`, 9 | }); 10 | 11 | function saveSettings(settingKey, settingValue) { 12 | nconf.set(settingKey, settingValue); 13 | nconf.save(); 14 | } 15 | 16 | function readSettings(settingKey) { 17 | nconf.load(); 18 | return nconf.get(settingKey); 19 | } 20 | 21 | module.exports = { 22 | saveSettings, 23 | readSettings, 24 | }; 25 | -------------------------------------------------------------------------------- /src/handlers/menu.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const { remote, shell, ipcRenderer } = require('electron'); 4 | const AppConfig = require('../configuration'); 5 | 6 | const { Menu, app } = remote; 7 | 8 | const Common = require('../common');; 9 | 10 | class MenuHandler { 11 | create() { 12 | const template = this.getTemplate(remote.process.platform); 13 | if (template) { 14 | const menuFromTemplate = Menu.buildFromTemplate(template); 15 | Menu.setApplicationMenu(menuFromTemplate); 16 | } 17 | } 18 | 19 | getTemplate(platform) { 20 | const darwinTemplate = [ 21 | { 22 | label: Common.ELECTRONIC_WECHAT, 23 | submenu: [ 24 | { 25 | label: Common.MENU.about, 26 | selector: 'orderFrontStandardAboutPanel:', 27 | }, 28 | { 29 | type: 'separator', 30 | }, 31 | { 32 | label: Common.MENU.service, 33 | submenu: [], 34 | }, 35 | { 36 | type: 'separator', 37 | }, 38 | { 39 | label: Common.MENU.hide, 40 | accelerator: 'Command+H', 41 | selector: 'hide:', 42 | }, 43 | { 44 | label: Common.MENU.hideOther, 45 | accelerator: 'Command+Alt+H', 46 | selector: 'hideOtherApplications:', 47 | }, 48 | { 49 | label: Common.MENU.showAll, 50 | selector: 'unhideAllApplications:', 51 | }, 52 | { 53 | type: 'separator', 54 | }, 55 | { 56 | label: Common.MENU.pref, 57 | click: MenuHandler._preference, 58 | }, 59 | { 60 | type: 'separator', 61 | }, 62 | { 63 | label: Common.MENU.quit, 64 | accelerator: 'Command+Q', 65 | click: MenuHandler._quitApp, 66 | }, 67 | ], 68 | }, 69 | { 70 | label: Common.MENU.edit, 71 | submenu: [ 72 | { 73 | label: Common.MENU.undo, 74 | accelerator: 'Command+Z', 75 | selector: 'undo:', 76 | }, 77 | { 78 | label: Common.MENU.redo, 79 | accelerator: 'Shift+Command+Z', 80 | selector: 'redo:', 81 | }, 82 | { 83 | type: 'separator', 84 | }, 85 | { 86 | label: Common.MENU.cut, 87 | accelerator: 'Command+X', 88 | selector: 'cut:', 89 | }, 90 | { 91 | label: Common.MENU.copy, 92 | accelerator: 'Command+C', 93 | selector: 'copy:', 94 | }, 95 | { 96 | label: Common.MENU.paste, 97 | accelerator: 'Command+V', 98 | selector: 'paste:', 99 | }, 100 | { 101 | label: Common.MENU.selectAll, 102 | accelerator: 'Command+A', 103 | selector: 'selectAll:', 104 | }, 105 | { 106 | type: 'separator', 107 | }, 108 | { 109 | label: Common.MENU.searchContacts, 110 | accelerator: 'Command+F', 111 | click: () => { 112 | $('#search_bar input')[0].focus(); 113 | }, 114 | }, 115 | ], 116 | }, 117 | { 118 | label: Common.MENU.view, 119 | submenu: [ 120 | { 121 | label: Common.MENU.reload, 122 | accelerator: 'Command+R', 123 | click: MenuHandler._reload, 124 | }, 125 | { 126 | label: Common.MENU.devtool, 127 | accelerator: 'Alt+Command+I', 128 | click: MenuHandler._devTools, 129 | }, 130 | ], 131 | }, 132 | { 133 | label: Common.MENU.window, 134 | submenu: [ 135 | { 136 | label: Common.MENU.min, 137 | accelerator: 'Command+M', 138 | selector: 'performMiniaturize:', 139 | }, 140 | { 141 | label: Common.MENU.close, 142 | accelerator: 'Command+W', 143 | selector: 'performClose:', 144 | }, 145 | { 146 | label: Common.MENU.toggleFullScreen, 147 | accelerator: 'Ctrl+Command+F', 148 | click: (item, focusedWindow) => { 149 | if (focusedWindow) { 150 | focusedWindow.setFullScreen(!focusedWindow.isFullScreen()); 151 | } 152 | }, 153 | }, 154 | { 155 | type: 'separator', 156 | }, 157 | { 158 | label: Common.MENU.allFront, 159 | selector: 'arrangeInFront:', 160 | }, 161 | ], 162 | }, 163 | { 164 | label: Common.MENU.help, 165 | submenu: [ 166 | { 167 | label: Common.MENU.repo, 168 | click: MenuHandler._github, 169 | }, 170 | { 171 | type: 'separator', 172 | }, { 173 | label: Common.MENU.feedback, 174 | click: MenuHandler._githubIssues, 175 | }, { 176 | label: Common.MENU.checkRelease, 177 | click: MenuHandler._update, 178 | }], 179 | }, 180 | ]; 181 | const linuxTemplate = [ 182 | { 183 | label: Common.MENU.window, 184 | submenu: [ 185 | { 186 | label: Common.MENU.pref, 187 | click: MenuHandler._preference, 188 | }, 189 | { 190 | label: Common.MENU.reload, 191 | accelerator: 'Ctrl+R', 192 | click: MenuHandler._reload, 193 | }, 194 | { 195 | label: Common.MENU.toggleFullScreen, 196 | accelerator: 'F11', 197 | click: (item, focusedWindow) => { 198 | if (focusedWindow) { 199 | focusedWindow.setFullScreen(!focusedWindow.isFullScreen()); 200 | } 201 | }, 202 | }, 203 | { 204 | type: 'separator', 205 | }, 206 | { 207 | label: Common.MENU.searchContacts, 208 | accelerator: 'Ctrl+F', 209 | click: () => { 210 | $('#search_bar input')[0].focus(); 211 | }, 212 | }, 213 | { 214 | label: Common.MENU.devtool, 215 | accelerator: 'Ctrl+Shift+I', 216 | click: MenuHandler._devTools, 217 | }, 218 | { 219 | type: 'separator', 220 | }, 221 | { 222 | label: Common.MENU.quit, 223 | accelerator: 'Ctrl+Q', 224 | click: MenuHandler._quitApp, 225 | }, 226 | ], 227 | }, 228 | { 229 | label: Common.MENU.help, 230 | submenu: [ 231 | { 232 | label: Common.MENU.repo, 233 | click: MenuHandler._github, 234 | }, 235 | { 236 | label: Common.MENU.repo_fork, 237 | click: MenuHandler._forkerGithub, 238 | }, 239 | { 240 | type: 'separator', 241 | }, { 242 | label: Common.MENU.feedback, 243 | click: MenuHandler._githubIssues, 244 | }, { 245 | label: Common.MENU.feedback_forker, 246 | click: MenuHandler._forkerGithubIssues, 247 | }, { 248 | label: Common.MENU.checkRelease, 249 | click: MenuHandler._update, 250 | }], 251 | }, 252 | ]; 253 | 254 | if (platform === 'darwin') { 255 | return darwinTemplate; 256 | } else if (platform === 'linux') { 257 | return linuxTemplate; 258 | } 259 | } 260 | 261 | static _quitApp() { 262 | ipcRenderer.send('exit'); 263 | } 264 | 265 | static _reload() { 266 | ipcRenderer.send('reload'); 267 | } 268 | 269 | static _devTools() { 270 | remote.getCurrentWindow().toggleDevTools(); 271 | } 272 | 273 | static _github() { 274 | shell.openExternal(Common.GITHUB); 275 | } 276 | 277 | static _forkerGithub() { 278 | shell.openExternal(Common.FORKER_GITHUB); 279 | } 280 | 281 | static _githubIssues() { 282 | shell.openExternal(Common.GITHUB_ISSUES); 283 | } 284 | 285 | static _forkerGithubIssues() { 286 | shell.openExternal(Common.FORKER_GITHUB_ISSUES); 287 | } 288 | 289 | static _update() { 290 | ipcRenderer.send('update'); 291 | } 292 | 293 | static _preference() { 294 | ipcRenderer.send('open-settings-window'); 295 | } 296 | } 297 | module.exports = MenuHandler; 298 | -------------------------------------------------------------------------------- /src/handlers/message.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const qs = require('querystring'); 4 | const url = require('url'); 5 | 6 | class MessageHandler { 7 | handleRedirectMessage(origin) { 8 | return qs.parse(url.parse(origin).query).requrl || origin; 9 | } 10 | } 11 | 12 | module.exports = MessageHandler; 13 | -------------------------------------------------------------------------------- /src/handlers/update.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Zhongyi on 3/25/16. 3 | */ 4 | 5 | 'use strict'; 6 | 7 | const { dialog, shell, app, nativeImage } = require('electron'); 8 | const AppConfig = require('../configuration'); 9 | const https = require('https'); 10 | const path = require('path'); 11 | 12 | const Common = require('../common');; 13 | 14 | class UpdateHandler { 15 | checkForUpdate(version, silent) { 16 | UpdateHandler.CHECKED = true; 17 | const promise = new Promise((res, rej) => { 18 | if (Common.ELECTRON === app.getName()) { 19 | rej(Common.UPDATE_ERROR_ELECTRON); 20 | } 21 | const req = https.get({ 22 | host: Common.GITHUB_API_HOST, 23 | headers: { 'user-agent': Common.USER_AGENT }, 24 | path: Common.FORKER_GITHUB_API_RELEASE_LATEST_PATH, 25 | }, (response) => { 26 | let body = ''; 27 | response.on('data', (d) => { 28 | body += d; 29 | }); 30 | response.on('end', () => { 31 | this._parseUpdateData(body, version, res, rej); 32 | }); 33 | }); 34 | req.on('error', (err) => { 35 | rej(Common.UPDATE_ERROR_NETWORK); 36 | }); 37 | req.end(); 38 | }).then((fetched) => { 39 | this.showDialog(fetched.name, fetched.description, 'Update', (response) => { 40 | if (!response) return; 41 | shell.openExternal(fetched.url); 42 | }); 43 | }).catch((message) => { 44 | if (silent) return; 45 | if (!message) { 46 | message = Common.UPDATE_ERROR_UNKNOWN; 47 | } 48 | this.showDialog(Common.UPDATE_NA_TITLE, message, 'OK'); 49 | }); 50 | } 51 | 52 | showDialog(message, detail, positiveButton, callback) { 53 | const iconImage = nativeImage.createFromPath(path.join(__dirname, '../../assets/icon.png')); 54 | dialog.showMessageBox({ 55 | type: 'info', 56 | buttons: ['Cancel', positiveButton], 57 | defaultId: 1, 58 | cancelId: 0, 59 | title: message, 60 | message, 61 | detail, 62 | icon: iconImage, 63 | }, callback); 64 | } 65 | 66 | _parseUpdateData(body, version, res, rej) { 67 | try{ 68 | const data = JSON.parse(body); 69 | if (!data || !data.tag_name) rej(Common.UPDATE_ERROR_EMPTY_RESPONSE); 70 | const fetched = { 71 | version: data.tag_name, 72 | is_prerelease: data.prerelease, 73 | name: data.name||`有可用更新 当前版本(${version} > ${data.tag_name})`, 74 | url: data.html_url, 75 | description: data.body, 76 | }; 77 | const versionRegex = /^(v|V)[0-9]+\.[0-9]+\.*[0-9]*$/; 78 | if (versionRegex.test(fetched.version) && this.compareVersion(fetched.version,version) && !fetched.is_prerelease) { 79 | res(fetched); 80 | } else { 81 | rej(Common.UPDATE_ERROR_LATEST(version)); 82 | } 83 | } 84 | catch(e){ 85 | rej(Common.UPDATE_ERROR_UNKNOWN); 86 | } 87 | } 88 | 89 | compareVersion (v1,v2) { 90 | v1 = v1.match(/v(.*)?\.(.*)?\.(.*)?/) 91 | v2 = v2.match(/v(.*)?\.(.*)?\.(.*)?/) 92 | for (let i = 1;i < 4;i++) { 93 | if(parseInt(v1[i]) > parseInt(v2[i])){ 94 | return true 95 | } 96 | if (parseInt(v1[i]) < parseInt(v2[i])) { 97 | return false 98 | } 99 | } 100 | return false 101 | } 102 | } 103 | 104 | UpdateHandler.CHECKED = false; 105 | 106 | module.exports = UpdateHandler; 107 | -------------------------------------------------------------------------------- /src/inject/badge_count.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Zhongyi on 4/12/16. 3 | */ 4 | 'use strict'; 5 | const { ipcRenderer } = require('electron'); 6 | 7 | class BadgeCount { 8 | static init() { 9 | setInterval(() => { 10 | let count = 0; 11 | $('.icon.web_wechat_reddot_middle').each(function () { 12 | count += parseInt(this.textContent, 10); 13 | }); 14 | if (count > 0) { 15 | ipcRenderer.send('badge-changed', count.toString()); 16 | } else { 17 | ipcRenderer.send('badge-changed', ''); 18 | } 19 | }, 1500); 20 | } 21 | } 22 | 23 | module.exports = BadgeCount; 24 | -------------------------------------------------------------------------------- /src/inject/chat_historys.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by kooritea on 26/6/18. 3 | */ 4 | 'use strict'; 5 | const { ipcRenderer } = require('electron'); 6 | var easyIDB = require('../lib/easyIDB.js') 7 | const Common = require('../common'); 8 | 9 | class ChatHistorys{ 10 | 11 | init(){ 12 | this.initIndexDB() 13 | //打开indexedDB 14 | //读取里面的所有历史消息,以NickName作为键存放在全局变量AllChatHistorys里面 15 | this.initEvent() 16 | //初始化滚动条事件 17 | } 18 | 19 | async initIndexDB(){ 20 | let self = this 21 | if (!angular.element('.header').scope().account) { 22 | setTimeout(()=>{ 23 | this.initIndexDB() 24 | }, 1000); 25 | return 26 | } 27 | this.selfUserName = angular.element('.header').scope().account.UserName 28 | this.DBName = 'Uin'+angular.element('.header').scope().account.Uin 29 | this.myIDB = await easyIDB({name:this.DBName,ver:1},[ 30 | { 31 | name:'history', 32 | indexs:[ 33 | { 34 | name:'PYQuanPin', 35 | unique:false 36 | }, 37 | { 38 | name:'RemarkPYQuanPin', 39 | unique:false 40 | }, 41 | { 42 | name:'NickName', 43 | unique:false 44 | } 45 | ], 46 | option:{ 47 | keyPath:'id',//主键,默认 'id' 48 | autoIncrement:true//是否自增,默认 true 49 | } 50 | } 51 | ]) 52 | angular.element('#chatArea').scope().$watch('currentUser',this.restoreChatContent.bind(this)); 53 | this.initData() 54 | } 55 | 56 | initData(){ 57 | //把所有历史信息读取到内存中 58 | try{ 59 | this.myIDB.DB.tmp=false 60 | if(!this.myIDB.DB.name){ 61 | throw 'error' 62 | } 63 | } 64 | catch(e){ 65 | console.log(e) 66 | console.log('indexDB未初始化完成,1s后重试(2)') 67 | setTimeout(()=>{ 68 | this.getHistory(user) 69 | },1000) 70 | return 71 | } 72 | this.AllChatHistorys={} 73 | window.AllChatHistorys = this.AllChatHistorys 74 | this.myIDB.get('history').then((data)=>{ 75 | for(let item of data){ 76 | if(!this.AllChatHistorys[item.NickName]){ 77 | this.AllChatHistorys[item.NickName]={ 78 | chats:[], 79 | get:0//已读取到聊天对象的条数 80 | } 81 | } 82 | this.AllChatHistorys[item.NickName].chats.push(item) 83 | } 84 | this.readAllChats=true 85 | }) 86 | } 87 | 88 | initEvent(){ 89 | let target = angular.element('#chatArea .scroll-wrapper>.scroll-content')[0] 90 | let loadHisStatus=document.createElement("div"); 91 | loadHisStatus.style="position:absolute;width:calc(100% - 38px);top:-20px;text-align:center;height:13px;font-size:13px;line-height:13px;" 92 | loadHisStatus.id="loadHisStatus" 93 | angular.element('#chatArea .scroll-wrapper')[0].appendChild(loadHisStatus) 94 | 95 | let $getHistory = document.createElement("p"); 96 | $getHistory.innerHTML="加载历史记录" 97 | $getHistory.onclick=() => { 98 | this.getHistory(angular.element('#chatArea').scope().currentUser) 99 | } 100 | target.onmousewheel = (event)=>{ 101 | this.debounce(()=>{ 102 | if(angular.element('#chatArea').scope().currentUser === '2233') return 103 | if(target.scrollTop === 0 && event.deltaY<0){ 104 | if(!this.lockScroll){ 105 | this.lockscroll=true 106 | loadHisStatus.innerHTML="加载中" 107 | loadHisStatus.style.top='20px' 108 | setTimeout(()=>{ 109 | this.getHistory(angular.element('#chatArea').scope().currentUser) 110 | }) 111 | } 112 | } 113 | },500) 114 | }; 115 | } 116 | 117 | 118 | //监听message:add:success事件,截取聊天记录的灰调函数 119 | saveHistory({type,originMsg}){//保存聊天记录到indexDB 120 | if(originMsg.sendByLocal&&type === 'add') return // 本地发送的消息等发送成功事件再保存 121 | let msg = new Object(originMsg) 122 | //ChatHistorys.lock(msg, 'MMCancelUploadFileFunc', ''); 123 | if(!msg.Content){ 124 | return 125 | } 126 | try{ 127 | this.myIDB.DB.tmp=false 128 | if(!this.myIDB.DB.name){ 129 | throw 'error' 130 | } 131 | } 132 | catch(e){ 133 | console.log(e) 134 | console.log('indexDB未初始化完成,1s后重试(1)') 135 | setTimeout(()=>{ 136 | this.saveHistory(msg) 137 | },1000) 138 | return 139 | } 140 | setTimeout(()=>{ 141 | msg.MMStatus=2 142 | if (msg.MMTime) { 143 | msg.MMTime=ChatHistorys.timestampToTime(msg.MMDisplayTime) 144 | } 145 | if(msg.sendByLocal||msg.FromUserName===this.selfUserName){//自己发送的消息(包括手机上发送的) 146 | msg.sendByLocal = true 147 | if(msg.ToUserName === 'filehelper'){ 148 | msg.NickName = 'filehelper' 149 | msg.PYQuanPin ='filehelper' 150 | msg.RemarkPYQuanPin ='filehelper' 151 | } 152 | else{ 153 | msg.NickName = msg.FromUserName==='filehelper'?'filehelper':window._contacts[msg.ToUserName].NickName 154 | msg.PYQuanPin = msg.FromUserName==='filehelper'?'filehelper':window._contacts[msg.ToUserName].PYQuanPin 155 | msg.RemarkPYQuanPin = msg.FromUserName==='filehelper'?'filehelper':window._contacts[msg.ToUserName].RemarkPYQuanPin 156 | } 157 | } 158 | else{ 159 | msg.NickName = msg.ToUserName==='filehelper'?'filehelper':window._contacts[msg.FromUserName].NickName 160 | msg.PYQuanPin = msg.ToUserName==='filehelper'?'filehelper':window._contacts[msg.FromUserName].PYQuanPin 161 | msg.RemarkPYQuanPin = msg.ToUserName==='filehelper'?'filehelper':window._contacts[msg.FromUserName].RemarkPYQuanPin 162 | } 163 | if(/@@/.test(msg.FromUserName)){ 164 | //群聊需要记录发送者的信息 165 | //在群的memberlist里查找 166 | let info = msg.Content.match(/@(.*)?:(.*)?/) 167 | let members= window._contacts[msg.FromUserName].MemberList 168 | for(let member of members){ 169 | if(member.UserName === '@'+info[1]){ 170 | msg.MMActualSenderNickName = member.NickName 171 | msg.MMActualSenderPYQuanPin = member.PYQuanPin 172 | msg.MMActualSenderRemarkPYQuanPin = member.RemarkPYQuanPin 173 | break 174 | } 175 | } 176 | } 177 | if(msg.$$hashKey){ 178 | delete msg.$$hashKey 179 | } 180 | this.myIDB.push('history',msg) 181 | },500) 182 | } 183 | 184 | restoreChatContent(user) { 185 | const scope = angular.element('#chatArea').scope(); 186 | if (!scope.chatContent || scope.chatContent.length === 0) { 187 | this.getHistory(user) 188 | } 189 | } 190 | 191 | getHistory(user){ 192 | if(user==='2233') return; 193 | try{ 194 | const scope = angular.element('#chatArea').scope(); 195 | if(!this.readAllChats){ 196 | setTimeout(()=>{ 197 | this.restoreChatContent(user) 198 | },500) 199 | return 200 | } 201 | if(!user){ 202 | return 203 | } 204 | let chatsObj 205 | let loadHisStatus = document.getElementById('loadHisStatus') 206 | if(user === 'filehelper'){ 207 | if(!this.AllChatHistorys.filehelper){ 208 | loadHisStatus.innerHTML="已经没有了" 209 | loadHisStatus.style.top='-20px' 210 | return 211 | } 212 | chatsObj = this.AllChatHistorys.filehelper 213 | } 214 | else{ 215 | if(!this.AllChatHistorys[window._contacts[user].NickName]){ 216 | //没有聊天记录自然AllChatHistorys里没有对应的键 217 | this.lockscroll=true 218 | loadHisStatus.innerHTML="已经没有了" 219 | loadHisStatus.style.top='-20px' 220 | return 221 | } 222 | chatsObj = this.AllChatHistorys[window._contacts[user].NickName] 223 | } 224 | let start = chatsObj.chats.length - chatsObj.get - 1 225 | let end = start-4>=0?start-4:0; 226 | let his = chatsObj.chats 227 | for (let i=start;i>=end;i--) { 228 | if(his[i].MsgType === 10000){ 229 | //撤回消息的提示 230 | //暂时没找到复原方法 231 | chatsObj.get++ 232 | continue 233 | } 234 | if(/@@/.test(user)){ 235 | //群聊 236 | //根据NickName在群成员中查找MMActualSender 237 | his[i].MMPeerUserName = user; 238 | if(his[i].sendByLocal){ 239 | his[i].FromUserName = this.selfUserName; 240 | his[i].ToUserName = user 241 | his[i].MMActualSender = this.selfUserName 242 | } 243 | else{ 244 | for(let member of window._contacts[user].MemberList){ 245 | if(member.NickName == his[i].MMActualSenderNickName){ 246 | his[i].MMActualSender = member.UserName 247 | break 248 | } 249 | } 250 | his[i].FromUserName = user 251 | his[i].ToUserName = this.selfUserName; 252 | } 253 | 254 | } 255 | else{ 256 | //私聊 257 | if(his[i].sendByLocal){ 258 | //自己发的消息 259 | his[i].MMActualSender=this.selfUserName 260 | his[i].FromUserName = this.selfUserName 261 | his[i].ToUserName = user; 262 | } 263 | else{ 264 | his[i].MMActualSender=user 265 | his[i].FromUserName = user 266 | his[i].ToUserName = this.selfUserName; 267 | } 268 | his[i].MMPeerUserName = user; 269 | } 270 | his[i].MMUnread = false; 271 | if(/<msg><emoji fromusername =/.test(his[i].Content)){ 272 | //非商店表情 273 | ChatHistorys.lock(his[i], 'MMDigest', '[Emoticon]'); 274 | if (his[i].ImgHeight >= Common.EMOJI_MAXIUM_SIZE) { 275 | ChatHistorys.lock(his[i], 'MMImgStyle', { height: `${Common.EMOJI_MAXIUM_SIZE}px`, width: 'initial' }); 276 | } else if (his[i].ImgWidth >= Common.EMOJI_MAXIUM_SIZE) { 277 | ChatHistorys.lock(his[i], 'MMImgStyle', { width: `${Common.EMOJI_MAXIUM_SIZE}px`, height: 'initial' }); 278 | } 279 | } 280 | scope.chatContent.unshift(his[i]); 281 | chatsObj.get++ 282 | } 283 | if(chatsObj.chats.length!==chatsObj.get){ 284 | loadHisStatus.innerHTML="获取成功" 285 | } 286 | else{ 287 | loadHisStatus.innerHTML="已经没有了" 288 | } 289 | } 290 | catch(e){ 291 | loadHisStatus.innerHTML="获取失败" 292 | console.error(e) 293 | console.error(user) 294 | } 295 | setTimeout(()=>{ 296 | this.lockscroll=true 297 | document.getElementById('loadHisStatus').style.top='-20px' 298 | },1000) 299 | } 300 | 301 | clearHistory(){ 302 | this.myIDB.clearStore('history',function(){ 303 | alert('清除成功') 304 | }) 305 | } 306 | 307 | // getHistory(user){//PYQuanPin,RemarkPYQuanPin 308 | // try{ 309 | // if(!user){ 310 | // return 311 | // } 312 | // this.myIDB.DB.tmp=false 313 | // if(!this.myIDB.DB.name){ 314 | // throw 'error' 315 | // } 316 | // } 317 | // catch(e){ 318 | // console.log(e) 319 | // console.log('indexDB未初始化完成,1s后重试(2)') 320 | // setTimeout(()=>{ 321 | // this.getHistory(user) 322 | // },1000) 323 | // return 324 | // } 325 | // let NickName = user==='filehelper'?'filehelper':window._contacts[user].NickName 326 | // let PYQuanPin = user==='filehelper'?'filehelper':window._contacts[user].PYQuanPin 327 | // let RemarkPYQuanPin = user==='filehelper'?'filehelper':window._contacts[user].RemarkPYQuanPin 328 | // if(/@@/.test(user)){ 329 | // //群聊优先匹配NickName 330 | // if(NickName){ 331 | // return this.myIDB.get('history','NickName',NickName) 332 | // }else if(PYQuanPin){ 333 | // return this.myIDB.get('history','PYQuanPin',PYQuanPin) 334 | // }else if(RemarkPYQuanPin){ 335 | // return this.myIDB.get('history','RemarkPYQuanPin',RemarkPYQuanPin) 336 | // } 337 | // } 338 | // else{ 339 | // if(RemarkPYQuanPin){ 340 | // return this.myIDB.get('history','RemarkPYQuanPin',RemarkPYQuanPin) 341 | // } 342 | // else if(PYQuanPin){ 343 | // return this.myIDB.get('history','PYQuanPin',PYQuanPin) 344 | // } 345 | // else if(NickName){ 346 | // return this.myIDB.get('history','NickName',NickName) 347 | // } 348 | // } 349 | // } 350 | 351 | static lock(object, key, value) { 352 | return Object.defineProperty(object, key, { 353 | get: () => value, 354 | set: () => {}, 355 | }); 356 | } 357 | 358 | static timestampToTime(timestamp) { 359 | let date = new Date(timestamp * 1000);//时间戳为10位需*1000,时间戳为13位的话不需乘1000 360 | let Y = date.getFullYear() + '-'; 361 | let M = (date.getMonth()+1 < 10 ? '0'+(date.getMonth()+1) : date.getMonth()+1) + '-'; 362 | let D = date.getDate() + ' '; 363 | let h = date.getHours() + ':'; 364 | let m = date.getMinutes() + ':'; 365 | let s = date.getSeconds(); 366 | return Y+M+D+h+m+s; 367 | } 368 | 369 | debounce(func){//防抖 370 | clearTimeout(this.timer) 371 | this.timer = setTimeout(()=>{ 372 | func() 373 | },300) 374 | } 375 | } 376 | module.exports = ChatHistorys; 377 | -------------------------------------------------------------------------------- /src/inject/css.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Zhongyi on 2/23/16. 3 | */ 4 | 'use strict'; 5 | const Common = require('../common'); 6 | 7 | class CSSInjector { 8 | } 9 | 10 | CSSInjector.commonCSS = ` 11 | .emoji{ 12 | margin:0 2px; 13 | } 14 | .login{ 15 | -webkit-app-region: drag; 16 | } 17 | .action{ 18 | -webkit-app-region: no-drag; 19 | cursor: pointer!important; 20 | } 21 | div.header{ 22 | padding:0px 18px 18px 18px; 23 | } 24 | div .header #miniFrame{ 25 | height:30px; 26 | display: inline-block; 27 | -webkit-app-region: no-drag; 28 | position: absolute; 29 | } 30 | div .header #miniFrame:hover>div{ 31 | top:0; 32 | } 33 | div .header #miniFrame div{ 34 | position:relative; 35 | height:30px; 36 | top:-30px; 37 | transition:top 0.1s; 38 | display:inline-block; 39 | } 40 | div .header #miniFrame img{ 41 | float:left; 42 | width:20px; 43 | height:20px; 44 | margin:5px 5px 0 0; 45 | -webkit-app-region: no-drag; 46 | } 47 | div .header #miniFrame img:hover{ 48 | background-color: rgba(255,255,255,0.1); 49 | box-shadow: 0px 0px 10px #888888; 50 | } 51 | div.download_entry{ 52 | display:none; 53 | } 54 | div.box_hd .title_wrap{ 55 | z-index:1000; 56 | } 57 | div.box_hd { 58 | -webkit-app-region: drag; 59 | } 60 | div.box_hd .ext{ 61 | -webkit-app-region: no-drag; 62 | } 63 | div.panel.give_me .nav_view{ 64 | top:166px; 65 | } 66 | div.title.poi { 67 | -webkit-app-region: no-drag; 68 | } 69 | div.header .avatar, div.header .info { 70 | -webkit-app-region: no-drag; 71 | padding-top:30px; 72 | } 73 | div.main { 74 | height: 100%; 75 | min-height: 0; 76 | padding-top: 0; 77 | } 78 | div.main_inner { 79 | max-width: none; 80 | min-width: 0; 81 | border-radius:0; 82 | } 83 | div.message_empty { 84 | margin-top: 50px; 85 | } 86 | div.img_preview_container div.img_opr_container { 87 | bottom: 50px; 88 | } 89 | p.copyright { 90 | display: none !important 91 | } 92 | a.web_wechat_screencut { 93 | display: none !important; 94 | } 95 | * { 96 | -webkit-user-select: none; 97 | cursor: default !important; 98 | -webkit-user-drag: none; 99 | } 100 | pre, input { 101 | -webkit-user-select: initial; 102 | cursor: initial !important; 103 | } 104 | .js_message_plain{ 105 | user-select: text; 106 | } 107 | html, body { 108 | width: 100%; 109 | height: 100%; 110 | overflow: hidden; 111 | } 112 | 113 | div.login_box { 114 | top: initial; 115 | left: initial; 116 | margin-left: initial; 117 | margin-top: initial; 118 | width: 100%; 119 | height: 100%; 120 | } 121 | div.login { 122 | min-width: 0; 123 | min-height: 0; 124 | width: 100%; 125 | height: 100%; 126 | overflow: hidden; 127 | } 128 | .login_box .refresh_qrcode_mask{ 129 | -webkit-app-region: no-drag; 130 | } 131 | div.lang, div.copyright { 132 | display: none !important 133 | } 134 | /* Group mention: user selection box */ 135 | div#userSelectionBox select option:hover { 136 | background: #eeeeee; 137 | } 138 | div#userSelectionBox select option { 139 | padding: 4px 10px; 140 | text-overflow: hidden; 141 | font-size: 14px; 142 | } 143 | .user_select_hint_text { 144 | padding: 4px 10px; 145 | font-size: 14px; 146 | background: #eeeeee; 147 | } 148 | div#userSelectionBox select { 149 | border: none; 150 | outline: none; 151 | height: inherit; 152 | } 153 | div#userSelectionBox { 154 | box-shadow: 1px 1px 10px #ababab; 155 | background: #fff; 156 | display: none; 157 | position: fixed; 158 | bottom: ${Common.MENTION_MENU_INITIAL_Y}px; 159 | left: ${Common.MENTION_MENU_INITIAL_X}px; 160 | } 161 | span.measure_text { 162 | padding-left: 20px; 163 | outline: 0; 164 | border: 0; 165 | font-size: 14px; 166 | } 167 | img.emojione { 168 | width: 20px; 169 | height: 20px; 170 | } 171 | div.jp-jplayer{ 172 | overflow-y:hidden; 173 | } 174 | div.jp-jplayer video{ 175 | background-color: #eee; 176 | } 177 | div.ngdialog-close{ 178 | -webkit-app-region: no-drag; 179 | cursor: pointer!important; 180 | } 181 | div.ngdialog-close:hover{ 182 | background-color: rgba(255,255,255,0.1); 183 | box-shadow: 0px 0px 10px #888888; 184 | } 185 | .dropdown_menu{ 186 | -webkit-app-region: no-drag; 187 | } 188 | .box_hd .ext{ 189 | -webkit-app-region: no-drag; 190 | } 191 | .login_box .association .button.button_primary{ 192 | -webkit-app-region: no-drag; 193 | } 194 | .login_box .association .button.button_default{ 195 | -webkit-app-region: no-drag; 196 | } 197 | .bubble_cont a{ 198 | user-select: text; 199 | } 200 | .bubble_cont .video img{ 201 | max-width: 200px; 202 | max-height: 300px; 203 | height: auto!important; 204 | width: auto!important; 205 | } 206 | .box_ft>.content{ 207 | height: calc(100% - 90px); 208 | } 209 | .box_ft>.content>.edit_area{ 210 | height:100%!important; 211 | } 212 | @media (max-width: 512px) { 213 | .panel { 214 | width: 75px !important; 215 | transition: width .3s; 216 | } 217 | .panel .header, 218 | .chat_item { 219 | padding: 8px 16px !important; 220 | } 221 | .header, 222 | .panel .tab, 223 | .search_bar, 224 | .chat_item .info, 225 | .chat_item .ext { 226 | display: none !important 227 | } 228 | .nav_view { 229 | top: 36px !important 230 | } 231 | .chat_item.active { 232 | border-left: 2px solid #02b300 !important 233 | } 234 | .action .desc { 235 | display: none; 236 | } 237 | .action .btn_send { 238 | width: 30px; 239 | } 240 | } 241 | `; 242 | 243 | CSSInjector.osxCSS = ` 244 | div.header div.avatar img.img { 245 | width: 24px; 246 | height: 24px; 247 | } 248 | div.header { 249 | padding-top: 38px; 250 | padding-bottom: 8px; 251 | } 252 | span.display_name { 253 | width: 172px !important; 254 | } 255 | @media (max-width: 512px) { 256 | .nav_view { 257 | top: 36px !important 258 | } 259 | } 260 | `; 261 | 262 | module.exports = CSSInjector; 263 | -------------------------------------------------------------------------------- /src/inject/emoji_parser.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by chenwl on 9/29/16. 3 | */ 4 | 5 | var emojione = require('emojione'); 6 | 7 | // 8 | const emojiSpanRegex = /<\/span>/g; 9 | 10 | function unicodeToString(point) { 11 | const offset = point - 0x10000; 12 | const lead = 0xd800 + (offset >> 10); 13 | const trail = 0xdc00 + (offset & 0x3ff); 14 | return String.fromCharCode(lead, trail); 15 | } 16 | 17 | class EmojiParser { 18 | static emojiSpanToString(str) { 19 | return str.replace(emojiSpanRegex, function(span, emojiHex) { 20 | const point = parseInt(emojiHex, 16); 21 | return unicodeToString(point); 22 | }); 23 | } 24 | 25 | static emojiToImage(str) { 26 | return emojione.unicodeToImage(EmojiParser.emojiSpanToString(str)); 27 | } 28 | } 29 | 30 | module.exports = EmojiParser; 31 | -------------------------------------------------------------------------------- /src/inject/mention_menu.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Zhongyi on 4/9/16. 3 | */ 4 | 5 | 'use strict'; 6 | const Common = require('../common'); 7 | const pinyin = require('pinyin'); 8 | 9 | class MentionMenu { 10 | 11 | static init() { 12 | const $box = $('
'); 13 | 14 | const $div = $('
'); 15 | $div.html(Common.MENTION_MENU_HINT_TEXT); 16 | $div.addClass('user_select_hint_text'); 17 | $box.append($div); 18 | 19 | const $select = $(' 38 | 39 | 40 | 41 | 42 | 43 | 44 |
45 |
    46 | 49 | 52 | 58 |
59 |
60 |
61 |
    62 | 65 | 68 | 74 |
75 |
76 |
77 |
    78 | 81 | 84 | 90 |
91 |
92 |
93 |
    94 | 97 | 100 | 106 |
107 |
108 |
109 |
    110 | 113 | 116 | 122 |
123 |
124 |
125 |
    126 | 129 | 132 | 140 |
141 |
142 |
143 |
    144 | 147 | 150 | 156 |
157 |
158 |
159 |
    160 | 163 | 166 | 169 |
170 |
171 |
172 |
    173 | 176 | 179 | 185 |
186 |
187 |
188 |
    189 | 192 | 195 | 201 |
202 |
203 |
204 |
    205 | 208 | 211 | 217 | 232 |
233 |
234 |
235 |
    236 | 239 | 242 | 248 | 251 |
252 |
253 |
254 |
    255 | 258 | 261 | 266 |
267 |
268 |
269 |
    270 | 273 | 276 | 282 | 287 |
288 |
289 |
290 | 291 | 546 | 547 | 548 | 549 | -------------------------------------------------------------------------------- /src/windows/views/splash.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Starting App 8 | 9 | 10 | 11 |
12 | 13 | Starting App 14 |
15 | 16 | 17 | 18 | --------------------------------------------------------------------------------