├── .env ├── .eslintignore ├── .gitignore ├── .prettierignore ├── ChangeLog.md ├── JSDOC.md ├── LICENCE-MAXIM.md ├── README-project.md ├── README.md ├── babel.config.js ├── config ├── rollup.sdk.config.js ├── webpack.dev.config.js ├── webpack.prod.config.js └── webpack.sdk.config.js ├── floo.sh ├── jsdoc.config.json ├── package.json ├── prettier.config.js ├── public ├── audio │ └── phone_ring.mp3 ├── favicon.ico ├── image │ ├── about_us-s.png │ ├── about_us.png │ ├── add.png │ ├── audio.png │ ├── audio_call.png │ ├── audio_call_switch.png │ ├── camera_off.png │ ├── camera_on.png │ ├── close.png │ ├── close_no_circle.png │ ├── contact-s.png │ ├── contact.png │ ├── conv-s.png │ ├── conv.png │ ├── edit.png │ ├── file.png │ ├── file2.png │ ├── group.png │ ├── hangup.png │ ├── im_closer.png │ ├── im_packup.png │ ├── im_popover.png │ ├── im_send_empty.png │ ├── im_send_full.png │ ├── im_tips.png │ ├── im_wechat.png │ ├── interaction.png │ ├── loc.png │ ├── loc2.png │ ├── logo4.png │ ├── logob.png │ ├── mic_off.png │ ├── mic_on.png │ ├── minimize.png │ ├── more.png │ ├── pickup.png │ ├── picture.png │ ├── play.png │ ├── qr.png │ ├── roster.png │ ├── search.png │ ├── setting-s.png │ ├── setting.png │ ├── speaker_off.png │ ├── speaker_on.png │ ├── splash.png │ ├── sw_off.png │ ├── sw_on.png │ ├── unverified.png │ ├── verify_failed.png │ └── verifyed.png └── index.html ├── src ├── App.vue ├── css │ ├── contact.css │ ├── content.css │ ├── header.css │ ├── index.css │ ├── layers.css │ ├── login.css │ ├── reg.css │ ├── setting.css │ ├── snackbar.css │ ├── support.css │ └── util.css ├── main.js ├── sdk │ ├── core │ │ ├── base │ │ │ ├── dataLogics.js │ │ │ ├── index.js │ │ │ ├── io │ │ │ │ ├── httpIo.js │ │ │ │ └── index.js │ │ │ ├── messageMaker.js │ │ │ └── messageReceiver.js │ │ ├── protocol │ │ │ ├── class │ │ │ │ ├── conversation.js │ │ │ │ ├── conversationunread.js │ │ │ │ ├── frame.js │ │ │ │ ├── groupnotice.js │ │ │ │ ├── index.js │ │ │ │ ├── info.js │ │ │ │ ├── messagebody.js │ │ │ │ ├── meta.js │ │ │ │ ├── notice.js │ │ │ │ ├── provision.js │ │ │ │ ├── rosternotice.js │ │ │ │ ├── rtcsignal.js │ │ │ │ ├── status.js │ │ │ │ ├── syncdl.js │ │ │ │ ├── syncul.js │ │ │ │ ├── unreaddl.js │ │ │ │ ├── usernotice.js │ │ │ │ └── xid.js │ │ │ ├── conversation.js │ │ │ ├── groupnotice.js │ │ │ ├── index.js │ │ │ ├── info.js │ │ │ ├── make.sh │ │ │ ├── messagebody.js │ │ │ ├── rosternotice.js │ │ │ ├── rtcsignal.js │ │ │ ├── usernotice.js │ │ │ ├── xid.js │ │ │ └── xsync.js │ │ └── rtc │ │ │ ├── janus.js │ │ │ └── socketCls.js │ ├── index.js │ ├── manage │ │ ├── dnsManager.js │ │ ├── groupManage.js │ │ ├── rosterManage.js │ │ ├── rtcManager.js │ │ ├── sysManage.js │ │ └── userManage.js │ ├── model │ │ ├── conversation.js │ │ ├── frame.js │ │ ├── index.js │ │ ├── message.js │ │ ├── meta.js │ │ ├── provision.js │ │ ├── rtcmessage.js │ │ ├── syncdl.js │ │ ├── syncul.js │ │ └── xid.js │ └── utils │ │ ├── cusEvent.js │ │ ├── encrypt.js │ │ ├── log.js │ │ ├── request.js │ │ ├── static.js │ │ ├── store.js │ │ ├── store │ │ ├── groupStore.js │ │ ├── infoStore.js │ │ ├── messageStore.js │ │ ├── noticeStore.js │ │ ├── recentStore.js │ │ ├── rosterStore.js │ │ └── storeBase.js │ │ ├── tools.js │ │ └── types.js └── ui │ ├── chatting │ ├── contact │ │ ├── conContact.vue │ │ ├── conConversation.vue │ │ └── index.vue │ ├── content │ │ ├── group │ │ │ ├── chat.vue │ │ │ ├── forward.vue │ │ │ ├── header.vue │ │ │ ├── index.vue │ │ │ ├── info.vue │ │ │ ├── inputer.vue │ │ │ ├── memberList.vue │ │ │ └── renderMsg.vue │ │ ├── index.vue │ │ ├── notice │ │ │ ├── groupApplyNotice.vue │ │ │ ├── groupInvitationNotice.vue │ │ │ ├── groupNotice.vue │ │ │ ├── index.vue │ │ │ ├── rosterNotice.vue │ │ │ └── systemNotice.vue │ │ ├── roster │ │ │ ├── chat.vue │ │ │ ├── forward.vue │ │ │ ├── header.vue │ │ │ ├── index.vue │ │ │ ├── info.vue │ │ │ ├── inputer.vue │ │ │ └── renderMsg.vue │ │ ├── setting │ │ │ └── index.vue │ │ └── verification │ │ │ └── index.vue │ ├── header │ │ └── index.vue │ └── index.vue │ ├── index.vue │ ├── layers │ ├── addfriend.vue │ ├── addpop.vue │ ├── audiocall.vue │ ├── callinvite.vue │ ├── changeappid.vue │ ├── creategroup.vue │ ├── groupsetting.vue │ ├── image2.vue │ ├── index.vue │ ├── joingroup.vue │ ├── linklogin.vue │ ├── qrcode.vue │ ├── search.vue │ ├── video.vue │ └── videocall.vue │ ├── login │ ├── bind.vue │ ├── bindacc.vue │ ├── bindreg.vue │ ├── codelogin.vue │ ├── index.vue │ ├── login.vue │ ├── qrlogin.vue │ ├── regedit.vue │ └── verifyinfo.vue │ ├── store │ ├── contact.js │ ├── content.js │ ├── forward.js │ ├── header.js │ ├── index.js │ ├── layer.js │ ├── login.js │ └── setting.js │ ├── support │ ├── content │ │ ├── group │ │ │ ├── chat.vue │ │ │ ├── index.vue │ │ │ ├── info.vue │ │ │ ├── inputer.vue │ │ │ └── renderMsg.vue │ │ ├── index.vue │ │ └── roster │ │ │ ├── chat.vue │ │ │ ├── index.vue │ │ │ ├── info.vue │ │ │ ├── inputer.vue │ │ │ └── renderMsg.vue │ ├── header │ │ └── index.vue │ ├── index.vue │ ├── loading │ │ └── index.vue │ ├── minimize │ │ └── index.vue │ ├── navigation │ │ └── index.vue │ └── skipping │ │ └── index.vue │ └── third │ ├── base64.js │ ├── crypto1.js │ ├── events.js │ ├── marked.min.js │ └── tools.js └── vue.config.js /.env: -------------------------------------------------------------------------------- 1 | # vue environment file, for sdk development 2 | sdk=source 3 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /src/sdk/core/rtc/janus.js 2 | /src/ui/third/marked.min.js 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *~ 2 | .DS_Store 3 | node_modules 4 | /dist 5 | dist.tar.gz 6 | test/unit/coverage 7 | test/e2e/reports 8 | selenium-debug.log 9 | package-lock.json 10 | /lanying-im-web 11 | lanying-im-web.tar.gz 12 | 13 | # wasm temp 14 | wasm/floo.ts 15 | build 16 | 17 | # local env files 18 | .env.local 19 | .env.*.local 20 | 21 | # Log files 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | yarn.lock 26 | .hintrc 27 | 28 | # Editor directories and files 29 | .idea 30 | *.suo 31 | *.ntvs* 32 | *.njsproj 33 | *.sln 34 | .vscode 35 | *.sw* 36 | 37 | # jsdoc output html dir 38 | docs/ 39 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # src/sdk/core/protocol 2 | src/im/ 3 | build 4 | dist 5 | node_modules 6 | lanying-im-web 7 | *.md 8 | -------------------------------------------------------------------------------- /LICENCE-MAXIM.md: -------------------------------------------------------------------------------- 1 | Maxim Licence 2 | Version 1.0, December 2018 3 | 4 | This software is part of MaxIM's new generation instant messaging (IM) system. 5 | 6 | You must make sure you have the right privilleges to use it. 7 | 8 | Copyright (C) 2018-2023 MaxIM.Top 9 | 10 | You may obtain a copy of the licence at http://www.maxim.top/LICENCE-MAXIM.md 11 | 12 | As an IM technology company, we are giving IM technologies and sources as well to our customers, 13 | 14 | we would dedicate ourselves to helping them. 15 | 16 | Max professional IM is our MaxIM 17 | 18 | Please contact eva@bmxlabs.com for more information. 19 | -------------------------------------------------------------------------------- /README-project.md: -------------------------------------------------------------------------------- 1 | ## 蓝莺IM web 版 2 | 3 | 蓝莺IM,是由[美信拓扑](https://www.lanyingim.com/)团队研发的新一代即时通讯云服务,SDK 设计简单集成方便,服务采用云原生技术和多云架构,私有云也可按月付费。 4 | 5 | 蓝莺IM APP 为方便体验试用蓝莺IMSDK 的 DemoApp。开发者可直接[在线试用](https://chat.lanyingim.com),也可在官网[下载页面](https://www.lanyingim.com/downloads/)选择试用所有客户端。 6 | 7 | DemoApp 是为了演示 IM SDK 调用而开发,也因此最好的开发方式为根据 DemoApp 找到功能,然后直接查看使用示例。 8 | 9 | [![Awesome](https://awesome.re/badge.svg)](https://awesome.re) [![Scc Count Badge](https://sloc.xyz/github/maxim-top/lanying-im-web/?category=total&avg-wage=1)](https://github.com/maxim-top/lanying-im-web/) [![Scc Count Badge](https://sloc.xyz/github/maxim-top/lanying-im-web/?category=code&avg-wage=1)](https://github.com/maxim-top/lanying-im-web/) 10 | 11 | ## 构建 12 | 13 | 本工程为标准 web 工程,推荐使用 yarn 来操作: 14 | 15 | 1. 构建工程 16 | ``` 17 | yarn build 18 | ``` 19 | 2. 本地运行 20 | 21 | 配置本地 `/etc/hosts` 表,增加一行 22 | 23 | ``` 24 | 127.0.0.1 dev.lanyingim.com 25 | ``` 26 | 27 | 然后再运行 28 | 29 | ``` 30 | yarn dev 31 | ``` 32 | 33 | 3. 打包应用 34 | ``` 35 | yarn pack 36 | ``` 37 | 38 | ## 开发自己的应用 39 | 40 | 请先修改蓝莺IM AppID 41 | 42 | 打开文件 `./src/App.vue`, 将默认 AppID: welovemaxim 更改为你的应用 AppID,此 AppID 为在[蓝莺IM 后台](https://console.lanyingim.com/)创建应用后获取。 43 | 44 | ## 代码风格 45 | 46 | 代码风格选择的 ESLint + Prettier,基本规则如下: 47 | 48 | 1. 所有缩进设置为 2 ,包括 Style Sheets 中的各种 css 语言文件、html 文件、JavaScript 文件和其它类型文件。 49 | 2. HTML 文件中 script 标签和 style 标签后的首行代码不缩进。 50 | 3. 函数名和花括号的空格 51 | - 函数声明时,函数名后不加括号; 52 | - 在函数表达式中 function 后面括号前不加空格; 53 | - 花括号中(插值表达式/解构赋值)首尾要增加空格。 54 | 55 | Webstorm 设置可参考[这里](https://www.wenyuanblog.com/blogs/webstorm-eslint-prettier-reformat-code.html)。 56 | 57 | ## 常见问题 58 | 59 | 1. 无法导入 flooim,提示 60 | 61 | ``` 62 | export 'flooim' was not found in '../im/floo-3.0.0' 63 | ``` 64 | 65 | 参考修改 babel.config.js,增加 sourceType: 'unambiguous' 设置: 66 | 67 | ``` 68 | module.exports = { 69 | presets: ["@vue/app", {sourceType: 'unambiguous'}], 70 | }; 71 | ``` 72 | 73 | 2. 找不到 long 模块,提示 74 | 75 | ``` 76 | module "third/long" is not defined 77 | ``` 78 | 79 | 这是因为 fsevent1 的问题,在 windows 下安装会失败,导致 npm 失败,可参考[这里](https://github.com/angular/angular/issues/13935),解决方法: 80 | 81 | ``` 82 | npm i -f 83 | ``` 84 | 85 | 3. vue3适配问题 86 | 87 | ``` 88 | The requested module '/src/im/floo-3.0.0.js' does not provide an export named 'default' 89 | ``` 90 | 91 | 需要通过 yarn 安装 vite-plugin-commonjs 和 vite-plugin-require-transform 两个插件。 92 | ![](https://docs.lanyingim.com/assets/vue3-import.jpg) 93 | 94 | ## 其他 95 | 96 | 了解更多信息可以阅读[在线文档](https://docs.lanyingim.com/),或者在本仓库提问,好好玩 :) 97 | 98 | --- 99 | 100 | **蓝莺IM 专业 SDK,私有云按月付费** 101 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 蓝莺IM SDK web 版 2 | 3 | 蓝莺IM,是由[美信拓扑](https://www.lanyingim.com/)团队研发的新一代即时通讯云服务,SDK 设计简单集成方便,服务采用云原生技术和多云架构,私有云也可按月付费。 4 | 5 | 本仓库是 IMSDK 的源码仓库,如果你只是开发自己的聊天 App,建议直接使用蓝莺IM web 版仓库 [lanying-im-web](https://github.com/maxim-top/lanying-im-web),也可以直接[在线试用](https://chat.lanyingim.com)。 6 | 7 | [![Awesome](https://awesome.re/badge.svg)](https://awesome.re) [![Scc Count Badge](https://sloc.xyz/github/maxim-top/floo-web/?category=total&avg-wage=1)](https://github.com/maxim-top/floo-web/) [![Scc Count Badge](https://sloc.xyz/github/maxim-top/floo-web/?category=code&avg-wage=1)](https://github.com/maxim-top/floo-web/) 8 | 9 | ## 开发 10 | 11 | 本工程为典型 yarn web 工程,开发时使用以下命令: 12 | 13 | 1. 安装所需依赖 14 | ``` 15 | yarn 16 | ``` 17 | 2. 启动本地服务 18 | ``` 19 | yarn dev 20 | ``` 21 | 3. 打包 SDK 22 | ``` 23 | yarn sdk 24 | ``` 25 | 26 | ## 发布 27 | 28 | 如要同步更新发布到 lanying-im-web,可先 clone lanying-im-web 仓库在本地根目录,运行命令 29 | 30 | ``` 31 | yarn release 32 | ``` 33 | 34 | 此命令会将打包后的 SDK 文件 floo-x.x.js 和其他 UI 代码更新到 lanying-im-web 文件夹, 35 | 然后进入 lanying-im-web 文件夹提交即可。 36 | 37 | ``` 38 | cd lanying-im-web && git commit -a 39 | ``` 40 | 41 | ## 代码风格 42 | 43 | 代码风格选择的 ESLint + Prettier,基本规则如下: 44 | 45 | 1. 所有缩进设置为 2 ,包括 Style Sheets 中的各种 css 语言文件、html 文件、JavaScript 文件和其它类型文件。 46 | 2. HTML 文件中 script 标签和 style 标签后的首行代码不缩进。 47 | 3. 函数名和花括号的空格 48 | - 函数声明时,函数名后不加括号; 49 | - 在函数表达式中 function 后面括号前不加空格; 50 | - 花括号中(插值表达式/解构赋值)首尾要增加空格。 51 | 52 | Webstorm 设置可参考[这里](https://www.wenyuanblog.com/blogs/webstorm-eslint-prettier-reformat-code.html)。 53 | 54 | ## 生成文档 55 | 56 | ``` 57 | yarn doc 58 | ``` 59 | 60 | ## 其他 61 | 62 | 了解更多信息可以阅读[在线文档](https://docs.lanyingim.com/quick-start/floo-web-quick-start.html),或者在本仓库提问,好好玩 :) 63 | 64 | --- 65 | 66 | **蓝莺IM 专业 SDK,私有云按月付费** 67 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | // let projectDir = path.join(__dirname, '.'); 4 | module.exports = { 5 | presets: ['@vue/app', { sourceType: 'unambiguous' }], 6 | plugins: [ 7 | [ 8 | 'component', 9 | { 10 | libraryName: 'element-ui', 11 | styleLibraryName: 'theme-chalk' 12 | } 13 | ], 14 | [ 15 | 'module-resolver', 16 | { 17 | alias: { 18 | // vue: path.join('vue', 'dist', 'vue.js'), 19 | '/': path.resolve(__dirname, '/'), 20 | '@src': path.resolve(__dirname, 'src'), 21 | '@proj': path.resolve(__dirname), 22 | components: path.resolve(__dirname, 'src/components/'), 23 | models: path.resolve(__dirname, 'src/models/'), 24 | services: path.resolve(__dirname, 'src/services/'), 25 | utils: path.resolve(__dirname, 'src/utils/') 26 | }, 27 | extensions: ['', '.js', '.json', '.vue', '.scss', '.css'] 28 | } 29 | ] 30 | ] 31 | }; 32 | -------------------------------------------------------------------------------- /config/rollup.sdk.config.js: -------------------------------------------------------------------------------- 1 | import resolve from '@rollup/plugin-node-resolve'; 2 | import cjs from '@rollup/plugin-commonjs'; 3 | import json from '@rollup/plugin-json'; 4 | import builtins from 'rollup-plugin-node-builtins'; 5 | import path from 'path'; 6 | 7 | const projectDir = path.join(__dirname, '..'); 8 | const srcDir = path.join(projectDir, 'src/sdk'); 9 | const buildDir = path.join(projectDir, 'build/sdk'); 10 | // const dependencies = Object.keys(require('../package.json').dependencies); 11 | 12 | export default [ 13 | { 14 | input: path.resolve(srcDir, 'index.js'), 15 | output: [ 16 | { 17 | file: path.resolve(buildDir, 'floo-3.0.0.u.js'), 18 | name: 'flooim', 19 | format: 'umd', 20 | sourcemap: false, 21 | globals: { 22 | 'socket.io-client': 'io', 23 | axios: 'axios', 24 | long: 'Long', 25 | 'query-string': 'queryString', 26 | 'protobufjs/light': '$protobuf', 27 | bufferutil: 'bufferutil', 28 | 'utf-8-validate': 'utf8Validate' 29 | } 30 | }, 31 | { 32 | file: path.resolve(buildDir, 'floo-3.0.0.c.js'), 33 | format: 'cjs' 34 | }, 35 | { 36 | file: path.resolve(buildDir, 'floo-3.0.0.e.js'), 37 | format: 'es' 38 | } 39 | ], 40 | // external: dependencies, 41 | onwarn: function (warning, warn) { 42 | // suppress eval warnings 43 | if (warning.code === 'EVAL' || (warning.code === 'CIRCULAR_DEPENDENCY' && !warning.importer.indexOf(path.normalize('node_modules/')))) { 44 | return; 45 | } 46 | warn(warning); 47 | }, 48 | plugins: [ 49 | resolve({ preferBuiltins: false }), 50 | cjs({ 51 | // include: /node_modules/, 52 | namedExports: { 53 | 'process/index.js': ['nextTick'], 54 | 'events/events.js': ['EventEmitter'], 55 | 'buffer/index.js': ['isBuffer'], 56 | 'axios/index.js': ['defaults', 'interceptors'], 57 | 'protobufjs/light.js': ['roots', 'Root'] 58 | } 59 | }), 60 | json(), 61 | builtins() 62 | ] 63 | } 64 | ]; 65 | -------------------------------------------------------------------------------- /config/webpack.dev.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | const CleanWebpackPlugin = require('clean-webpack-plugin'); 4 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 5 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 6 | 7 | const projectDir = path.join(__dirname, '..'); 8 | module.exports = (env) => { 9 | return { 10 | entry: { 11 | vendors: ['vue', 'vuex', 'axios', 'lodash'], 12 | app: path.join(projectDir, 'src', 'main.js') 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.js$/, 18 | exclude: /(node_modules|lib)/, 19 | loader: 'eslint-loader', 20 | options: { 21 | // eslint options (if necessary) 22 | } 23 | }, 24 | { 25 | test: /\.js$/, 26 | exclude: /node_modules/, 27 | use: { 28 | loader: 'babel-loader', 29 | options: { 30 | presets: ['@babel/preset-env'] 31 | } 32 | } 33 | } 34 | ] 35 | }, 36 | plugins: [ 37 | new webpack.HotModuleReplacementPlugin({}), 38 | new CleanWebpackPlugin(['dist'], { 39 | root: projectDir 40 | }), 41 | 42 | new webpack.DefinePlugin({ 43 | 'process.env.NODE_ENV': '"development"' 44 | }), 45 | 46 | new MiniCssExtractPlugin({ 47 | filename: '[name].css', 48 | chunkFilename: '[id].css' 49 | }), 50 | 51 | new HtmlWebpackPlugin({ 52 | template: path.join(projectDir, 'public', 'index.html'), 53 | filename: 'index.html', 54 | inject: 'body', 55 | minify: { 56 | removeComments: true, 57 | collapseWhitespace: true, 58 | removeAttributeQuotes: true 59 | }, 60 | favicon: path.join(projectDir, 'public', 'favicon.ico') 61 | }), 62 | 63 | new webpack.HotModuleReplacementPlugin(), 64 | 65 | new webpack.ProvidePlugin({ adapter: ['webrtc-adapter', 'default'] }) 66 | ], 67 | 68 | devServer: { 69 | port: 443, 70 | // inline: true, 71 | hot: true, 72 | open: true, 73 | host: '0.0.0.0' || 'dev.lanyingim.com', 74 | disableHostCheck: true, 75 | https: true 76 | // proxy: { 77 | // "**/*.do": { 78 | // target: devConfig.apiUrl, 79 | // onProxyReq (proxyReq) { 80 | // proxyReq.setHeader("host", url.parse(devConfig.apiUrl).host); 81 | // }, 82 | // }, 83 | // }, 84 | }, 85 | 86 | optimization: { 87 | splitChunks: { 88 | cacheGroups: { 89 | vendors: { 90 | name: 'vendors', 91 | chunks: 'initial', 92 | minChunks: 2 93 | }, 94 | manifest: { 95 | name: 'manifest', 96 | chunks: 'initial', 97 | minChunks: 2 98 | } 99 | } 100 | } 101 | }, 102 | 103 | stats: 'normal' 104 | }; 105 | }; 106 | -------------------------------------------------------------------------------- /config/webpack.prod.config.js: -------------------------------------------------------------------------------- 1 | const webpack = require('webpack'); 2 | const path = require('path'); 3 | 4 | const CleanWebpackPlugin = require('clean-webpack-plugin'); 5 | // const ExtractTextWebpackPlugin = require("extract-text-webpack-plugin"); 6 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 7 | const MiniCssExtractPlugin = require('mini-css-extract-plugin'); 8 | const TerserPlugin = require('terser-webpack-plugin'); 9 | const EncodingPlugin = require('webpack-encoding-plugin'); 10 | 11 | const projectDir = path.join(__dirname, '..'); 12 | module.exports = (env) => { 13 | return { 14 | entry: { 15 | vendors: ['vue', 'vuex', 'axios', 'lodash'], 16 | app: path.join(projectDir, 'src', 'main.js') 17 | }, 18 | module: { 19 | rules: [ 20 | { 21 | test: /\.js$/, 22 | exclude: /(node_modules|lib)/, 23 | loader: 'eslint-loader', 24 | options: { 25 | // eslint options (if necessary) 26 | } 27 | }, 28 | { 29 | test: /\.js$/, 30 | exclude: /node_modules/, 31 | use: { 32 | loader: 'babel-loader', 33 | options: { 34 | presets: ['@babel/preset-env'] 35 | } 36 | } 37 | } 38 | ] 39 | }, 40 | 41 | plugins: [ 42 | new CleanWebpackPlugin(['dist'], { 43 | root: projectDir 44 | }), 45 | 46 | new webpack.DefinePlugin({ 47 | 'process.env.NODE_ENV': '"production"' 48 | }), 49 | 50 | new MiniCssExtractPlugin({ 51 | filename: '[name].css', 52 | chunkFilename: '[id].css' 53 | }), 54 | 55 | new HtmlWebpackPlugin({ 56 | template: path.join(projectDir, 'public', 'index.html'), 57 | filename: 'index.html', 58 | inject: 'body', 59 | minify: { 60 | removeComments: true, 61 | collapseWhitespace: true, 62 | removeAttributeQuotes: true 63 | }, 64 | favicon: path.join(projectDir, 'public', 'favicon.ico') 65 | }), 66 | 67 | new EncodingPlugin({ 68 | encoding: 'utf8' 69 | }), 70 | 71 | new webpack.ProvidePlugin({ adapter: ['webrtc-adapter', 'default'] }) 72 | ], 73 | optimization: { 74 | splitChunks: { 75 | cacheGroups: { 76 | vendors: { 77 | name: 'vendors', 78 | chunks: 'initial', 79 | minChunks: 2 80 | }, 81 | manifest: { 82 | name: 'manifest', 83 | chunks: 'initial', 84 | minChunks: 2 85 | } 86 | } 87 | }, 88 | minimize: true, 89 | minimizer: [ 90 | new TerserPlugin({ 91 | extractComments: { 92 | condition: /^\**!|@preserve|@license|@cc_on/i, 93 | filename: (file, fileData) => { 94 | return file.replace(/\.(\w+)($|\?)/, '.$1.LICENSE$2'); 95 | }, 96 | banner: (licenseFile) => { 97 | return `License information can be found in ${licenseFile}`; 98 | } 99 | } 100 | }) 101 | ] 102 | } 103 | }; 104 | }; 105 | -------------------------------------------------------------------------------- /config/webpack.sdk.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const CleanWebpackPlugin = require('clean-webpack-plugin'); 3 | const TerserPlugin = require('terser-webpack-plugin'); 4 | 5 | const projectDir = path.join(__dirname, '..'); 6 | const srcDir = path.join(projectDir, 'src'); 7 | 8 | module.exports = (env) => { 9 | return { 10 | entry: './src/sdk/index.js', 11 | output: { 12 | path: path.resolve(projectDir, './build/sdk'), 13 | filename: 'floo-3.0.0.js', 14 | libraryTarget: 'umd', 15 | globalObject: 'this' 16 | }, 17 | module: { 18 | rules: [ 19 | { 20 | test: /\.js$/, 21 | exclude: /(node_modules|lib)/, 22 | loader: 'eslint-loader', 23 | options: { 24 | // eslint options (if necessary) 25 | } 26 | }, 27 | { 28 | test: /\.js$/, 29 | exclude: /(node_modules|bower_components)/, 30 | loader: 'babel-loader', 31 | options: { 32 | presets: ['@babel/preset-env'] 33 | } 34 | } 35 | ] 36 | }, 37 | plugins: [ 38 | new CleanWebpackPlugin(['build'], { 39 | verbose: false, 40 | exclude: ['img'] //不删除img静态资源 41 | }) 42 | ], 43 | devServer: { 44 | host: 'dev.lanyingim.com', 45 | port: '443', 46 | // open: true,//自动拉起浏览器 47 | hot: false, //热加载 48 | https: true, 49 | disableHostCheck: true, 50 | headers: { 51 | 'Access-Control-Allow-Origin': '*' 52 | } 53 | }, 54 | 55 | resolve: { 56 | alias: { 57 | '@src': path.join(projectDir, 'src'), 58 | '@model': path.join(projectDir, 'src/sdk/model'), 59 | '@utils': path.join(projectDir, 'src/sdk/utils'), 60 | '@core': path.join(projectDir, 'src/sdk/core') 61 | } 62 | }, 63 | 64 | optimization: { 65 | minimize: true, 66 | minimizer: [ 67 | new TerserPlugin({ 68 | extractComments: { 69 | condition: /^\**!|@preserve|@license|@cc_on/i, 70 | filename: (file, fileData) => { 71 | return file.replace(/\.(\w+)($|\?)/, '.$1.LICENSE$2'); 72 | }, 73 | banner: (licenseFile) => { 74 | return `License information can be found in ${licenseFile}`; 75 | } 76 | } 77 | }) 78 | ] 79 | } 80 | }; 81 | }; 82 | -------------------------------------------------------------------------------- /floo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | test ! -n "$1" && echo "Please specify project name" && exit 1 4 | 5 | project=$1 6 | echo "Creating new project: $project" 7 | 8 | pdir="./$project" 9 | mkdir -p $pdir 10 | im_dir="$pdir/src/im" 11 | 12 | cp LICENCE-MAXIM.md $pdir/ 13 | cp README-project.md $pdir/README.md 14 | cp ChangeLog.md $pdir/ 15 | cp .gitignore $pdir/ 16 | cp prettier.config.js $pdir/ 17 | cp .prettierignore $pdir/ 18 | cp babel.config.js $pdir/ 19 | cp vue.config.js $pdir/ 20 | 21 | cp package.json $pdir/ 22 | 23 | cp -R ./src $pdir/ 24 | cp -R ./public $pdir/ 25 | 26 | rm -rf $pdir/src/sdk 27 | 28 | mkdir -p $pdir/config 29 | mkdir -p $im_dir 30 | cp ./build/sdk/floo-*.js $im_dir 31 | gsed -i'' -E "s/(.*)\/sdk\/index(.*)/\1\/im\/floo-3.0.0\2/g" $pdir/src/ui/index.vue 32 | 33 | cp ./config/webpack.prod.config.js $pdir/config/ 34 | cp ./config/webpack.dev.config.js $pdir/config/ 35 | 36 | echo "done." 37 | -------------------------------------------------------------------------------- /jsdoc.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "source": { 3 | "includePattern": ".+\\.(js|md)$", // Only process file ending in .js, .jsdoc or .jsx 4 | "include": ["src/sdk", "JSDOC.md"], // Check all folders. 5 | "exclude": ["node_modules"] // Be gone, node_modules. 6 | }, 7 | "recurseDepth": 10, // Only go 10 levels deep. 8 | "opts": { 9 | "destination": "./docs/", // Where I want my docs to be generated. 10 | "recurse": true // Same as using -r or --recurse 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lanying-im-web", 3 | "version": "3.0.15", 4 | "private": true, 5 | "scripts": { 6 | "prebuild": "yarn", 7 | "build": "vue-cli-service build", 8 | "serve": "vue-cli-service serve", 9 | "lint": "vue-cli-service lint", 10 | "dev": "vue-cli-service serve --fix", 11 | "prod": "vue-cli-service build --mode production --fix", 12 | "prepackage": "yarn prod", 13 | "package": "tar zcvf lanying-im-web.tar.gz dist", 14 | "format": "yarn prettier -w .", 15 | "presdk": "yarn", 16 | "sdk": "webpack --mode production --config ./config/webpack.sdk.config.js", 17 | "sdk2": "rollup --config ./config/rollup.sdk.config.js", 18 | "prerelease": "yarn sdk", 19 | "release": "./floo.sh lanying-im-web", 20 | "doc": "./node_modules/.bin/jsdoc -c ./jsdoc.config.json", 21 | "doc-md": "node_modules/jsdoc-to-markdown/bin/cli.js src/*/*/*.js src/*/*/*/*.js > doc.md" 22 | }, 23 | "dependencies": { 24 | "core-js": "2", 25 | "jquery": "3.6.1", 26 | "vue-js-popover": "^1.2.1", 27 | "vuex": "^3.0.1", 28 | "webrtc-adapter": "^8.1.2", 29 | "crypto-js": "4.1.1" 30 | }, 31 | "devDependencies": { 32 | "@babel/core": "^7.16.0", 33 | "@babel/plugin-transform-modules-commonjs": "^7.9.0", 34 | "@babel/plugin-transform-runtime": "^7.6.2", 35 | "@babel/preset-env": "^7.16.4", 36 | "@rollup/plugin-commonjs": "^11.1.0", 37 | "@rollup/plugin-json": "^4.0.3", 38 | "@rollup/plugin-node-resolve": "^7.1.3", 39 | "@vue/cli-plugin-babel": "^3.11.0", 40 | "@vue/cli-plugin-eslint": "^4.1.2", 41 | "@vue/cli-service": "^3.11.0", 42 | "@vue/eslint-config-prettier": "^5.0.0", 43 | "assemblyscript": "^0.8.1", 44 | "axios": "^0.26.1", 45 | "babel-core": "^6.26.3", 46 | "babel-eslint": "^10.0.3", 47 | "babel-loader": "^8.2.3", 48 | "babel-plugin-component": "^1.1.1", 49 | "babel-plugin-module-resolver": "^3.2.0", 50 | "babel-preset-env": "^1.7.0", 51 | "babel-preset-es2015": "^6.24.1", 52 | "babel-preset-stage-0": "^6.24.1", 53 | "bufferutil": "^4.0.1", 54 | "clean-webpack-plugin": "^1.0.1", 55 | "element-ui": "^2.12.0", 56 | "eslint": "^6.4.0", 57 | "eslint-config-prettier": "^7.1.0", 58 | "eslint-loader": "^3.0.3", 59 | "eslint-plugin-html": "^5.0.3", 60 | "eslint-plugin-vue": "^6.1.2", 61 | "highlight.js": "^11.8.0", 62 | "html-webpack-plugin": "^4.2.0", 63 | "husky": "^4.3.7", 64 | "jsdoc": "^3.6.10", 65 | "jsdoc-to-markdown": "^7.1.1", 66 | "json-bigint": "1.0.0", 67 | "lodash": "^4.17.11", 68 | "long": "^4.0.0", 69 | "marked-highlight": "^2.0.6", 70 | "mini-css-extract-plugin": "^0.9.0", 71 | "moment": "^2.24.0", 72 | "prettier": "^2.2.1", 73 | "pretty-quick": "^3.1.0", 74 | "protobufjs": "^6.10.1", 75 | "qrcode": "^1.3.3", 76 | "query-string": "^6.2.0", 77 | "rollup-plugin-node-builtins": "^2.1.2", 78 | "socket.io-client": "^2.2.0", 79 | "strip-ansi": "^6.0.0", 80 | "terser-webpack-plugin": "^2.3.5", 81 | "typescript": "^3.7.4", 82 | "uglifyjs-webpack-plugin": "^2.2.0", 83 | "utf-8-validate": "^5.0.2", 84 | "vue": "^2.6.11", 85 | "vue-template-compiler": "^2.6.10", 86 | "webpack": "^4.43.0", 87 | "webpack-cli": "^3.3.11", 88 | "webpack-encoding-plugin": "^0.3.1", 89 | "wrapper-webpack-plugin": "^2.1.0" 90 | }, 91 | "eslintConfig": { 92 | "root": true, 93 | "env": { 94 | "node": true 95 | }, 96 | "extends": [ 97 | "plugin:vue/essential", 98 | "eslint:recommended", 99 | "prettier" 100 | ], 101 | "rules": { 102 | "no-unused-vars": "off" 103 | }, 104 | "parserOptions": { 105 | "parser": "babel-eslint" 106 | } 107 | }, 108 | "eslintIgnore": [ 109 | "/src/im/", 110 | "/dist/", 111 | "/node_modules/" 112 | ], 113 | "husky": { 114 | "hooks": { 115 | "pre-commit": "pretty-quick --staged" 116 | } 117 | }, 118 | "postcss": { 119 | "plugins": { 120 | "autoprefixer": {} 121 | } 122 | }, 123 | "browserslist": [ 124 | "> 1%", 125 | "last 2 versions", 126 | "not ie <= 8" 127 | ] 128 | } 129 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | printWidth: 180, 3 | semi: true, 4 | tabWidth: 2, 5 | useTabs: false, 6 | singleQuote: true, 7 | trailingComma: 'none', 8 | bracketSpacing: true, 9 | htmlWhitespaceSensitivity: 'ignore' 10 | }; 11 | -------------------------------------------------------------------------------- /public/audio/phone_ring.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/audio/phone_ring.mp3 -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/favicon.ico -------------------------------------------------------------------------------- /public/image/about_us-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/about_us-s.png -------------------------------------------------------------------------------- /public/image/about_us.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/about_us.png -------------------------------------------------------------------------------- /public/image/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/add.png -------------------------------------------------------------------------------- /public/image/audio.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/audio.png -------------------------------------------------------------------------------- /public/image/audio_call.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/audio_call.png -------------------------------------------------------------------------------- /public/image/audio_call_switch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/audio_call_switch.png -------------------------------------------------------------------------------- /public/image/camera_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/camera_off.png -------------------------------------------------------------------------------- /public/image/camera_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/camera_on.png -------------------------------------------------------------------------------- /public/image/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/close.png -------------------------------------------------------------------------------- /public/image/close_no_circle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/close_no_circle.png -------------------------------------------------------------------------------- /public/image/contact-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/contact-s.png -------------------------------------------------------------------------------- /public/image/contact.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/contact.png -------------------------------------------------------------------------------- /public/image/conv-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/conv-s.png -------------------------------------------------------------------------------- /public/image/conv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/conv.png -------------------------------------------------------------------------------- /public/image/edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/edit.png -------------------------------------------------------------------------------- /public/image/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/file.png -------------------------------------------------------------------------------- /public/image/file2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/file2.png -------------------------------------------------------------------------------- /public/image/group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/group.png -------------------------------------------------------------------------------- /public/image/hangup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/hangup.png -------------------------------------------------------------------------------- /public/image/im_closer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/im_closer.png -------------------------------------------------------------------------------- /public/image/im_packup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/im_packup.png -------------------------------------------------------------------------------- /public/image/im_popover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/im_popover.png -------------------------------------------------------------------------------- /public/image/im_send_empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/im_send_empty.png -------------------------------------------------------------------------------- /public/image/im_send_full.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/im_send_full.png -------------------------------------------------------------------------------- /public/image/im_tips.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/im_tips.png -------------------------------------------------------------------------------- /public/image/im_wechat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/im_wechat.png -------------------------------------------------------------------------------- /public/image/interaction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/interaction.png -------------------------------------------------------------------------------- /public/image/loc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/loc.png -------------------------------------------------------------------------------- /public/image/loc2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/loc2.png -------------------------------------------------------------------------------- /public/image/logo4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/logo4.png -------------------------------------------------------------------------------- /public/image/logob.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/logob.png -------------------------------------------------------------------------------- /public/image/mic_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/mic_off.png -------------------------------------------------------------------------------- /public/image/mic_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/mic_on.png -------------------------------------------------------------------------------- /public/image/minimize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/minimize.png -------------------------------------------------------------------------------- /public/image/more.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/more.png -------------------------------------------------------------------------------- /public/image/pickup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/pickup.png -------------------------------------------------------------------------------- /public/image/picture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/picture.png -------------------------------------------------------------------------------- /public/image/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/play.png -------------------------------------------------------------------------------- /public/image/qr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/qr.png -------------------------------------------------------------------------------- /public/image/roster.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/roster.png -------------------------------------------------------------------------------- /public/image/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/search.png -------------------------------------------------------------------------------- /public/image/setting-s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/setting-s.png -------------------------------------------------------------------------------- /public/image/setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/setting.png -------------------------------------------------------------------------------- /public/image/speaker_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/speaker_off.png -------------------------------------------------------------------------------- /public/image/speaker_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/speaker_on.png -------------------------------------------------------------------------------- /public/image/splash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/splash.png -------------------------------------------------------------------------------- /public/image/sw_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/sw_off.png -------------------------------------------------------------------------------- /public/image/sw_on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/sw_on.png -------------------------------------------------------------------------------- /public/image/unverified.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/unverified.png -------------------------------------------------------------------------------- /public/image/verify_failed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/verify_failed.png -------------------------------------------------------------------------------- /public/image/verifyed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/public/image/verifyed.png -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 蓝莺IM - 专业SDK,私有云按月付费 9 | 10 | 11 | 12 |
13 |
14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/css/contact.css: -------------------------------------------------------------------------------- 1 | .contact { 2 | float: left; 3 | background: white; 4 | overflow: hidden; 5 | width: 240px; 6 | height: 100%; 7 | border-radius: 10px 0px 0px 10px; 8 | } 9 | 10 | .contact-root { 11 | height: 100%; 12 | overflow-y: auto; 13 | } 14 | 15 | .contact .header { 16 | height: 22px; 17 | padding: 10px 20px; 18 | font-size: 16px; 19 | line-height: 22px; 20 | background: white; 21 | border: none; 22 | } 23 | 24 | .contact .header span { 25 | float: right; 26 | line-height: 22px; 27 | color: #83898f; 28 | } 29 | 30 | .contact .list .item { 31 | height: 24px; 32 | line-height: 24px; 33 | padding: 8px 12px; 34 | } 35 | 36 | .contact .list .item:hover { 37 | background: rgba(176, 208, 253, 0.3); 38 | } 39 | 40 | .contact .list .item .avatar { 41 | width: 24px; 42 | height: 24px; 43 | overflow: hidden; 44 | border-radius: 3px; 45 | float: left; 46 | } 47 | 48 | .contact .list .item .name { 49 | float: left; 50 | height: 24px; 51 | line-height: 22px; 52 | font-size: 14px; 53 | margin-left: 15px; 54 | color: #333; 55 | text-overflow: ellipsis; 56 | overflow: hidden; 57 | max-width: 66.6%; 58 | } 59 | 60 | .contact .l_conversation { 61 | width: 240px; 62 | height: 100%; 63 | overflow-y: auto; 64 | } 65 | 66 | .contact .l_conversation .sel { 67 | background: rgba(176, 208, 253, 0.3); 68 | } 69 | 70 | .contact .l_conversation .item { 71 | height: 60px; 72 | font-size: 12px; 73 | position: relative; 74 | border-bottom: 1px solid #ececec; 75 | } 76 | 77 | .contact .l_conversation .item .unread_number { 78 | position: absolute; 79 | top: 8px; 80 | left: 36px; 81 | height: 14px; 82 | color: white; 83 | background: red; 84 | font-size: 10px; 85 | font-weight: bold; 86 | padding: 0 3px; 87 | border-radius: 7px; 88 | line-height: 14px; 89 | min-width: 8px; 90 | text-align: center; 91 | } 92 | 93 | .contact .l_conversation .item .avatar { 94 | height: 32px; 95 | width: 32px; 96 | border-radius: 3px; 97 | margin: 14px 10px 0px 10px; 98 | float: left; 99 | } 100 | 101 | .contact .l_conversation .item .name { 102 | margin-left: 60px; 103 | line-height: 20px; 104 | padding-top: 12px; 105 | color: #000; 106 | text-overflow: ellipsis; 107 | overflow: hidden; 108 | max-width: 40%; 109 | white-space: nowrap; 110 | } 111 | 112 | .contact .l_conversation .item .last_msg_time { 113 | position: absolute; 114 | right: 10px; 115 | line-height: 10px; 116 | margin-top: -14px; 117 | font-size: 10px; 118 | color: #8798a4; 119 | } 120 | 121 | .contact .l_conversation .item .last_msg { 122 | height: 20px; 123 | line-height: 20px; 124 | margin-left: 60px; 125 | color: #8798a4; 126 | text-overflow: ellipsis; 127 | white-space: nowrap; 128 | overflow: hidden; 129 | max-width: 66.6%; 130 | } 131 | 132 | .contact .l_conversation .item .last_msg .at_tips { 133 | color: red; 134 | } 135 | -------------------------------------------------------------------------------- /src/css/header.css: -------------------------------------------------------------------------------- 1 | .header { 2 | height: 48px; 3 | background: #47b6ff; 4 | border-radius: 10px 10px 0px 0px; 5 | text-overflow: ellipsis; 6 | overflow: hidden; 7 | border-bottom: 1px solid #e6e6e6; 8 | } 9 | 10 | .header .searchArea { 11 | height: 48px; 12 | width: 280px; 13 | float: left; 14 | } 15 | 16 | .header .searchArea input { 17 | width: 180px; 18 | height: 24px; 19 | float: left; 20 | margin-left: 10px; 21 | margin-top: 11px; 22 | border: 1px solid rgba(255, 255, 255, 0.5); 23 | border-radius: 100px; 24 | background-image: url(/image/search.png); 25 | filter: brightness(0) invert(1); 26 | background-repeat: no-repeat; 27 | background-size: 17px 17px; 28 | background-position: 8px center; 29 | background-color: rgba(255, 255, 255, 0); 30 | text-indent: 40px; 31 | font-size: 14px; 32 | } 33 | 34 | .header .searchArea .addBtn { 35 | float: left; 36 | width: 20px; 37 | height: 20px; 38 | background-image: url(/image/add.png); 39 | cursor: pointer; 40 | margin: 14px 0px 0px 20px; 41 | background-size: 20px 20px; 42 | filter: brightness(0) invert(1); 43 | } 44 | 45 | .header .tab { 46 | height: 48px; 47 | float: left; 48 | } 49 | 50 | .header .tab .stab { 51 | float: left; 52 | margin: 14px 36px 0px 0px; 53 | } 54 | 55 | .header .tab .stab .unread_number { 56 | position: relative; 57 | left: -5px; 58 | color: white; 59 | background: red; 60 | font-size: 10px; 61 | font-weight: bold; 62 | padding: 0 5px; 63 | border-radius: 10px; 64 | line-height: 14px; 65 | min-width: 8px; 66 | text-align: center; 67 | } 68 | 69 | .header .tab .stab img { 70 | width: 20px; 71 | height: 20px; 72 | filter: brightness(0) invert(1); 73 | } 74 | 75 | .header .profile { 76 | height: 48px; 77 | float: right; 78 | } 79 | 80 | .header .profile .proAvater { 81 | float: right; 82 | width: 30px; 83 | height: 30px; 84 | border-radius: 15px; 85 | background-size: 30px 30px; 86 | background-repeat: no-repeat; 87 | margin-right: 10px; 88 | margin-top: 9px; 89 | } 90 | 91 | .header .profile .proname { 92 | float: right; 93 | margin-right: 20px; 94 | line-height: 20px; 95 | margin-top: 14px; 96 | font-size: 16px; 97 | color: white; 98 | text-overflow: ellipsis; 99 | overflow: hidden; 100 | max-width: 155px; 101 | } 102 | 103 | .header .supportname { 104 | float: right; 105 | margin-right: 20px; 106 | line-height: 20px; 107 | margin-left: 6px; 108 | font-size: 16px; 109 | color: white; 110 | } 111 | -------------------------------------------------------------------------------- /src/css/index.css: -------------------------------------------------------------------------------- 1 | @import './util.css'; 2 | @import './login.css'; 3 | @import './reg.css'; 4 | 5 | @import './header.css'; 6 | @import './contact.css'; 7 | @import './content.css'; 8 | @import './support.css'; 9 | @import './setting.css'; 10 | @import './layers.css'; 11 | @import './snackbar.css'; 12 | 13 | /**** log 的css 先放这里 ***/ 14 | #slog { 15 | position: absolute; 16 | top: 5px; 17 | left: 5px; 18 | font-size: 8px; 19 | height: 55px; 20 | line-height: 11px; 21 | color: #eee; 22 | } 23 | -------------------------------------------------------------------------------- /src/css/reg.css: -------------------------------------------------------------------------------- 1 | .rege { 2 | position: absolute; 3 | top: 50%; 4 | left: 50%; 5 | width: 300px; 6 | height: 500px; 7 | margin-left: -200px; 8 | margin-top: -250px; 9 | padding: 50px 30px; 10 | box-shadow: 0 8px 10px 0 #f0f2f5; 11 | border-radius: 20px; 12 | background: #fff; 13 | } 14 | 15 | .rege .reg_header { 16 | height: 30px; 17 | font-size: 16px; 18 | } 19 | 20 | .rege .reg_header .text1 { 21 | margin-left: 38px; 22 | color: #333; 23 | margin-top: 8px; 24 | line-height: 28px; 25 | font-size: 20px; 26 | } 27 | 28 | .rege .reg_header .text2 { 29 | margin-left: 38px; 30 | color: #8d8300; 31 | line-height: 17px; 32 | font-size: 12px; 33 | } 34 | 35 | .rege .tab { 36 | height: 330x; 37 | margin-top: 30px; 38 | line-height: 33px; 39 | font-size: 24px; 40 | } 41 | 42 | .rege .tab .reg { 43 | margin-left: 26px; 44 | font-size: 14px; 45 | } 46 | 47 | .rege .iptFrame { 48 | background: #fafbfc; 49 | border: 1px solid #e6e8eb; 50 | border-radius: 12px; 51 | height: 28px; 52 | margin-top: 20px; 53 | overflow: hidden; 54 | } 55 | 56 | .rege .iptFrame input { 57 | background: #fafbfc; 58 | border: none; 59 | width: 100%; 60 | height: 100%; 61 | font-size: 14px; 62 | text-indent: 10px; 63 | outline: none; 64 | } 65 | 66 | .rege .regBtn { 67 | line-height: 48px; 68 | background: #f7e700; 69 | border-radius: 12px; 70 | font-size: 16px; 71 | text-align: center; 72 | margin-top: 20px; 73 | } 74 | -------------------------------------------------------------------------------- /src/css/setting.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/src/css/setting.css -------------------------------------------------------------------------------- /src/css/snackbar.css: -------------------------------------------------------------------------------- 1 | #lanying-snackbar { 2 | visibility: hidden; 3 | min-width: 250px; 4 | margin-left: -125px; 5 | bottom: 30px; 6 | background-color: #333; 7 | color: #fff; 8 | text-align: center; 9 | border-radius: 2px; 10 | padding: 16px; 11 | position: fixed; 12 | z-index: 1; 13 | left: 50%; 14 | font-size: 17px; 15 | } 16 | 17 | #lanying-snackbar.show { 18 | visibility: visible; 19 | -webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s; 20 | animation: fadein 0.5s, fadeout 0.5s 2.5s; 21 | } 22 | 23 | @-webkit-keyframes fadein { 24 | from { 25 | bottom: 0; 26 | opacity: 0; 27 | } 28 | to { 29 | bottom: 30px; 30 | opacity: 1; 31 | } 32 | } 33 | 34 | @keyframes fadein { 35 | from { 36 | bottom: 0; 37 | opacity: 0; 38 | } 39 | to { 40 | bottom: 30px; 41 | opacity: 1; 42 | } 43 | } 44 | 45 | @-webkit-keyframes fadeout { 46 | from { 47 | bottom: 30px; 48 | opacity: 1; 49 | } 50 | to { 51 | bottom: 0; 52 | opacity: 0; 53 | } 54 | } 55 | 56 | @keyframes fadeout { 57 | from { 58 | bottom: 30px; 59 | opacity: 1; 60 | } 61 | to { 62 | bottom: 0; 63 | opacity: 0; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import App from './App.vue'; 3 | 4 | import store from './ui/store'; 5 | import axios from 'axios'; 6 | 7 | import './css/index.css'; 8 | 9 | import ElementUI from 'element-ui'; 10 | 11 | Vue.config.productionTip = false; 12 | Vue.prototype.axios = axios; 13 | Vue.use(ElementUI); 14 | 15 | import VPopover from 'vue-js-popover'; 16 | Vue.use(VPopover, { tooltip: true }); 17 | 18 | Vue.prototype.serr = (err) => { 19 | let msg = '操作失败'; 20 | if (err.message) { 21 | msg = err.code ? '错误码:' + err.code + '\n错误:' + err.message : '错误:' + err.message; 22 | } 23 | alert(msg); 24 | }; 25 | 26 | new Vue({ 27 | render: (h) => h(App), 28 | store 29 | }).$mount('#app'); 30 | -------------------------------------------------------------------------------- /src/sdk/core/protocol/class/conversation.js: -------------------------------------------------------------------------------- 1 | import jsonDescriptor from '../conversation'; 2 | import protobuf from 'protobufjs/light'; 3 | 4 | var root = protobuf.Root.fromJSON(jsonDescriptor); 5 | const Conv = root.lookupType('im.floo.protobuf.Conversation'); 6 | 7 | const decode = (bytes) => Conv.decode(bytes); 8 | 9 | const encode = (obj) => Conv.encode(obj).finish(); 10 | 11 | export { decode, encode }; 12 | -------------------------------------------------------------------------------- /src/sdk/core/protocol/class/conversationunread.js: -------------------------------------------------------------------------------- 1 | import { decode as xidDecode } from './xid'; 2 | 3 | import protobuf from 'protobufjs/light'; 4 | import jsonDescriptor from '../xsync'; 5 | 6 | var root = protobuf.Root.fromJSON(jsonDescriptor); 7 | const ConversationUnread = root.lookupType('im.floo.protobuf.ConversationUnread'); 8 | 9 | const decode = (bytes) => { 10 | if (typeof bytes === 'undefined') { 11 | return bytes; 12 | } 13 | const ret = ConversationUnread.decode(bytes); 14 | ret.xid && (ret.xid = xidDecode(ret.xid)); 15 | return ret; 16 | }; 17 | 18 | export { decode }; 19 | -------------------------------------------------------------------------------- /src/sdk/core/protocol/class/frame.js: -------------------------------------------------------------------------------- 1 | // 有其他调用 2 | import protobuf from 'protobufjs/light'; 3 | import jsonDescriptor from '../xsync'; 4 | import { STATIC_FRAME_COMMAND } from '../../../utils/static'; 5 | import { decode as unreadDecode, encode as unreaddlEncode } from './unreaddl'; 6 | import { decode as syncdlDecode } from './syncdl'; 7 | import { encode as synculEncode } from './syncul'; 8 | import { decode as noticeDecode, encode as noticeEncode } from './notice'; 9 | import { decode as provisionDecode, encode as provisionEncode } from './provision'; 10 | 11 | var root = protobuf.Root.fromJSON(jsonDescriptor); 12 | const Frame = root.lookupType('im.floo.protobuf.Frame'); 13 | 14 | // const pubkey = infoStore.getAesKey(); 15 | 16 | const decode = (bytes) => { 17 | bytes = new Uint8Array(bytes); 18 | const ret = Frame.decode(bytes); 19 | const { command, payload } = ret; 20 | if (command === STATIC_FRAME_COMMAND.UNREAD) { 21 | ret.payload = unreadDecode(payload); 22 | } else if (command === STATIC_FRAME_COMMAND.SYNC) { 23 | ret.payload = syncdlDecode(payload); 24 | } else if (command === STATIC_FRAME_COMMAND.NOTICE) { 25 | ret.payload = noticeDecode(payload); 26 | } else if (command === STATIC_FRAME_COMMAND.PROVISION) { 27 | ret.payload = provisionDecode(payload); 28 | } 29 | return ret; 30 | }; 31 | 32 | const encode = (obj) => { 33 | const { payload, command } = obj; 34 | if (payload) { 35 | if (command === STATIC_FRAME_COMMAND.UNREAD) { 36 | obj.payload = unreaddlEncode(payload); 37 | } else if (command === STATIC_FRAME_COMMAND.SYNC) { 38 | obj.payload = synculEncode(payload); 39 | } else if (command === STATIC_FRAME_COMMAND.NOTICE) { 40 | obj.payload = noticeEncode(payload); 41 | } else if (command === STATIC_FRAME_COMMAND.PROVISION) { 42 | obj.payload = provisionEncode(payload); 43 | } 44 | } 45 | const ret = Frame.encode(obj).finish(); 46 | return ret; 47 | }; 48 | 49 | export { decode, encode }; 50 | -------------------------------------------------------------------------------- /src/sdk/core/protocol/class/groupnotice.js: -------------------------------------------------------------------------------- 1 | import { encode as xidEncode } from './xid'; 2 | import protobuf from 'protobufjs/light'; 3 | import jsonDescriptor from '../groupnotice'; 4 | // import { encode as statusEncode, decode as statusDecode } from './status'; 5 | 6 | var root = protobuf.Root.fromJSON(jsonDescriptor); 7 | const GroupNotice = root.lookupType('im.floo.protobuf.GroupNotice'); 8 | 9 | const decode = (bytes) => { 10 | const ret = GroupNotice.decode(bytes); // 也都已经搞好了。。 11 | // ret.from = xidDecode(ret.from); 12 | // ret.xid = xidDecode(ret.xid); 13 | // const sto = ret.to || []; 14 | // const toArr = []; 15 | // sto.forEach(item => { 16 | // toArr.push(xidDecode(item)); 17 | // }) 18 | // ret.to = toArr; 19 | return ret; 20 | }; 21 | 22 | const encode = (obj) => { 23 | // obj.xid = xidEncode(obj.xid); 24 | obj.from = xidEncode(obj.from); 25 | const toArr = []; 26 | const sto = obj.to || []; 27 | sto.forEach((item) => { 28 | toArr.push(xidEncode(item)); 29 | }); 30 | obj.to = toArr; 31 | return GroupNotice.encode(obj).finish(); 32 | }; 33 | 34 | export { decode, encode }; 35 | -------------------------------------------------------------------------------- /src/sdk/core/protocol/class/index.js: -------------------------------------------------------------------------------- 1 | import * as provision from './provision'; 2 | // import * as syncdl from './syncdl'; 3 | import * as syncul from './syncul'; 4 | // import * as unreaddl from './unreaddl'; 5 | import * as frame from './frame'; 6 | // import { decode as xidDecode } from './xid'; 7 | // import { decode as noticeDecode } from './notice'; 8 | // import { STATIC_FRAME_COMMAND } from '@utils/static'; 9 | 10 | // use int64 as long in protobuf 11 | import protobuf from 'protobufjs/light'; 12 | import Long from 'long'; 13 | protobuf.util.Long = Long; 14 | protobuf.configure(); 15 | 16 | const { encode: provisionEncode } = provision; 17 | const { encode: frameEncode, decode: frameDecode } = frame; 18 | // const { decode: unreaddlDecode } = unreaddl; 19 | // const { decode: syncdlDecode } = syncdl; 20 | const { encode: synculEncode } = syncul; 21 | 22 | const packProvision = (provisionObj) => { 23 | const payload = provisionObj.payload; 24 | const bytes = provisionEncode(payload); 25 | provisionObj.payload = bytes; 26 | return frameEncode(provisionObj); 27 | }; 28 | 29 | // 消息,等,meta定 30 | const packSyncul = (synculObj) => { 31 | const { payload } = synculObj; 32 | const bytes = synculEncode(payload); 33 | synculObj.payload = bytes; 34 | return frameEncode(synculObj); 35 | }; 36 | 37 | const packUnreadul = () => { 38 | const obj = { 39 | vsn: 0, 40 | compress_method: 0, 41 | command: 0 42 | }; 43 | return frameEncode(obj); 44 | }; 45 | 46 | const unpack = (bytes) => { 47 | const frmObj = frameDecode(bytes); 48 | return frmObj; 49 | }; 50 | 51 | export { packSyncul, packProvision, unpack, packUnreadul, frameEncode }; 52 | -------------------------------------------------------------------------------- /src/sdk/core/protocol/class/info.js: -------------------------------------------------------------------------------- 1 | import jsonDescriptor from '../info'; 2 | import protobuf from 'protobufjs/light'; 3 | 4 | var root = protobuf.Root.fromJSON(jsonDescriptor); 5 | const Info = root.lookupType('im.floo.protobuf.Info'); 6 | 7 | const decode = (bytes) => Info.decode(bytes); 8 | 9 | const encode = (obj) => Info.encode(obj).finish(); 10 | 11 | export { decode, encode }; 12 | -------------------------------------------------------------------------------- /src/sdk/core/protocol/class/messagebody.js: -------------------------------------------------------------------------------- 1 | import { constr as xidConstr } from './xid'; 2 | import protobuf from 'protobufjs/light'; 3 | import jsonDescriptor from '../messagebody'; 4 | 5 | var root = protobuf.Root.fromJSON(jsonDescriptor); 6 | const MessageBody = root.lookupType('im.floo.protobuf.MessageBody'); 7 | // const MessageOperation = root.lookupType("top.maxim.protobuf.MessageOperation"); 8 | 9 | const decode = (bytes) => { 10 | const ret = MessageBody.decode(bytes); 11 | //里边其他东西已经解析了。。 12 | return ret; 13 | }; 14 | 15 | const encode = (obj) => { 16 | // 其他已经encode了,这里不需要额外处理了,都是坑啊。。 17 | // obj.operation = MessageOperation.encode(obj.operation).finish(); 18 | // obj.from = xidEncode(obj.from); 19 | // obj.to = xidEncode(obj.to); 20 | return MessageBody.encode(obj).finish(); 21 | }; 22 | 23 | const constr = (obj) => { 24 | obj.xid = xidConstr(obj.xid); 25 | return MessageBody.create(obj); 26 | }; 27 | 28 | export { decode, encode, constr }; 29 | -------------------------------------------------------------------------------- /src/sdk/core/protocol/class/meta.js: -------------------------------------------------------------------------------- 1 | import protobuf from 'protobufjs/light'; 2 | import jsonDescriptor from '../xsync'; 3 | import { STATIC_META_NAMESPACE } from '../../../utils/static'; 4 | import { decode as msgBodyDecode, encode as msgBodyEncode } from './messagebody'; 5 | import { decode as groupnoticeDecode, encode as groupnoticeEncode } from './groupnotice'; 6 | import { decode as rosternoticeDecode, encode as rosternoticeEncode } from './rosternotice'; 7 | import { decode as usernoticeDecode, encode as usernoticeEncode } from './usernotice'; 8 | import { decode as infoDecode, encode as infoEncode } from './info'; 9 | import { decode as convDecode, encode as convEncode } from './conversation'; 10 | import { decode as rtcSignalDecode, encode as rtcSignalEncode } from './rtcsignal'; 11 | 12 | var root = protobuf.Root.fromJSON(jsonDescriptor); 13 | const Meta = root.lookupType('im.floo.protobuf.Meta'); 14 | 15 | const decode = (bytes) => { 16 | const ret = Meta.decode(bytes); 17 | const { ns, payload } = ret; 18 | if (ns === STATIC_META_NAMESPACE.MESSAGE) { 19 | ret.payload = msgBodyDecode(payload); 20 | } 21 | if (ns === STATIC_META_NAMESPACE.GROUP_NOTICE) { 22 | ret.payload = groupnoticeDecode(payload); 23 | } 24 | if (ns === STATIC_META_NAMESPACE.ROSTER_NOTICE) { 25 | ret.payload = rosternoticeDecode(payload); 26 | } 27 | if (ns === STATIC_META_NAMESPACE.USER_NOTICE) { 28 | ret.payload = usernoticeDecode(payload); 29 | } 30 | if (ns === STATIC_META_NAMESPACE.INFO) { 31 | ret.payload = infoDecode(payload); 32 | } 33 | if (ns === STATIC_META_NAMESPACE.CONVERSATION) { 34 | ret.payload = convDecode(payload); 35 | } 36 | if (ns === STATIC_META_NAMESPACE.PUSH) { 37 | ret.payload = msgBodyDecode(payload); 38 | } 39 | if (ns === STATIC_META_NAMESPACE.RTC_SIGNAL) { 40 | ret.payload = rtcSignalDecode(payload); 41 | } 42 | return ret; 43 | }; 44 | 45 | const encode = (obj) => { 46 | const { ns, payload } = obj; 47 | // typeof ns !== 'undefined' && smeta.setNs(ns); 48 | 49 | if (ns === STATIC_META_NAMESPACE.MESSAGE) { 50 | obj.payload = msgBodyEncode(payload); 51 | } 52 | if (ns === STATIC_META_NAMESPACE.GROUP_NOTICE) { 53 | obj.payload = groupnoticeEncode(payload); 54 | } 55 | if (ns === STATIC_META_NAMESPACE.ROSTER_NOTICE) { 56 | obj.payload = rosternoticeEncode(payload); 57 | } 58 | if (ns === STATIC_META_NAMESPACE.USER_NOTICE) { 59 | obj.payload = usernoticeEncode(payload); 60 | } 61 | if (ns === STATIC_META_NAMESPACE.INFO) { 62 | obj.payload = infoEncode(payload); 63 | } 64 | if (ns === STATIC_META_NAMESPACE.CONVERSATION) { 65 | obj.payload = convEncode(payload); 66 | } 67 | if (ns === STATIC_META_NAMESPACE.PUSH) { 68 | obj.payload = msgBodyEncode(payload); 69 | } 70 | if (ns === STATIC_META_NAMESPACE.RTC_SIGNAL) { 71 | obj.payload = rtcSignalEncode(payload); 72 | } 73 | return Meta.encode(obj).finish(); 74 | }; 75 | 76 | const constr = (obj) => { 77 | const { ns, payload } = obj; 78 | 79 | if (ns === STATIC_META_NAMESPACE.MESSAGE) { 80 | obj.payload = msgBodyEncode(payload); 81 | } 82 | if (ns === STATIC_META_NAMESPACE.GROUP_NOTICE) { 83 | obj.payload = groupnoticeEncode(payload); 84 | } 85 | if (ns === STATIC_META_NAMESPACE.ROSTER_NOTICE) { 86 | obj.payload = rosternoticeEncode(payload); 87 | } 88 | if (ns === STATIC_META_NAMESPACE.USER_NOTICE) { 89 | obj.payload = usernoticeEncode(payload); 90 | } 91 | if (ns === STATIC_META_NAMESPACE.INFO) { 92 | obj.payload = infoEncode(payload); 93 | } 94 | if (ns === STATIC_META_NAMESPACE.CONVERSATION) { 95 | obj.payload = convEncode(payload); 96 | } 97 | if (ns === STATIC_META_NAMESPACE.PUSH) { 98 | obj.payload = msgBodyEncode(payload); 99 | } 100 | if (ns === STATIC_META_NAMESPACE.RTC_SIGNAL) { 101 | obj.payload = rtcSignalEncode(payload); 102 | } 103 | return Meta.create(obj); 104 | }; 105 | 106 | export { decode, encode, constr }; 107 | -------------------------------------------------------------------------------- /src/sdk/core/protocol/class/notice.js: -------------------------------------------------------------------------------- 1 | import { decode as xidDecode } from './xid'; 2 | import protobuf from 'protobufjs/light'; 3 | import jsonDescriptor from '../xsync'; 4 | 5 | var root = protobuf.Root.fromJSON(jsonDescriptor); 6 | const Notice = root.lookupType('im.floo.protobuf.Notice'); 7 | 8 | const decode = (bytes) => { 9 | const xid = xidDecode(bytes); 10 | // const ret = Notice.decode(bytes); 11 | return { xid }; 12 | }; 13 | 14 | const encode = (obj) => Notice.encode(obj).finish(); 15 | 16 | export { decode, encode }; 17 | -------------------------------------------------------------------------------- /src/sdk/core/protocol/class/provision.js: -------------------------------------------------------------------------------- 1 | import protobuf from 'protobufjs/light'; 2 | import jsonDescriptor from '../xsync'; 3 | 4 | var root = protobuf.Root.fromJSON(jsonDescriptor); 5 | const Provision = root.lookupType('im.floo.protobuf.Provision'); 6 | 7 | const decode = (bytes) => { 8 | const ret = Provision.decode(bytes); 9 | return ret; 10 | }; 11 | 12 | const encode = (obj) => { 13 | return Provision.encode(obj).finish(); 14 | }; 15 | 16 | export { decode, encode }; 17 | -------------------------------------------------------------------------------- /src/sdk/core/protocol/class/rosternotice.js: -------------------------------------------------------------------------------- 1 | import protobuf from 'protobufjs/light'; 2 | import jsonDescriptor from '../rosternotice'; 3 | 4 | var root = protobuf.Root.fromJSON(jsonDescriptor); 5 | const RosterNotice = root.lookupType('im.floo.protobuf.RosterNotice'); 6 | 7 | const decode = (bytes) => { 8 | const ret = RosterNotice.decode(bytes); 9 | return ret; 10 | }; 11 | 12 | const encode = (obj) => { 13 | return RosterNotice.encode(obj).finish(); 14 | }; 15 | 16 | export { decode, encode }; 17 | -------------------------------------------------------------------------------- /src/sdk/core/protocol/class/rtcsignal.js: -------------------------------------------------------------------------------- 1 | import protobuf from 'protobufjs/light'; 2 | import jsonDescriptor from '../rtcsignal'; 3 | 4 | var root = protobuf.Root.fromJSON(jsonDescriptor); 5 | const RtcSignal = root.lookupType('im.floo.protobuf.RtcSignal'); 6 | 7 | const decode = (bytes) => RtcSignal.decode(bytes); 8 | 9 | const encode = (obj) => RtcSignal.encode(obj).finish(); 10 | 11 | export { decode, encode }; 12 | -------------------------------------------------------------------------------- /src/sdk/core/protocol/class/status.js: -------------------------------------------------------------------------------- 1 | import protobuf from 'protobufjs/light'; 2 | import jsonDescriptor from '../xsync'; 3 | 4 | var root = protobuf.Root.fromJSON(jsonDescriptor); 5 | const Status = root.lookupType('im.floo.protobuf.Status'); 6 | 7 | const decode = (bytes) => bytes && Status.decode(bytes); 8 | 9 | const encode = (obj) => obj && Status.encode(obj).finish(); 10 | 11 | const constr = (obj) => Status.create(obj); 12 | 13 | export { decode, encode, constr }; 14 | -------------------------------------------------------------------------------- /src/sdk/core/protocol/class/syncdl.js: -------------------------------------------------------------------------------- 1 | import { encode as metaEncode } from './meta'; 2 | import { decode as msgBodyDecode } from './messagebody'; 3 | import { decode as groupnoticeDecode } from './groupnotice'; 4 | import { decode as rosternoticeDecode } from './rosternotice'; 5 | import { decode as usernoticeDecode } from './usernotice'; 6 | import { decode as infoDecode } from './info'; 7 | import { decode as convDecode } from './conversation'; 8 | import { decode as rtcSignalDecode } from './rtcsignal'; 9 | import { STATIC_META_NAMESPACE } from '../../../utils/static'; 10 | import protobuf from 'protobufjs/light'; 11 | import jsonDescriptor from '../xsync'; 12 | 13 | const SyncDL = protobuf.Root.fromJSON(jsonDescriptor).lookupType('im.floo.protobuf.SyncDL'); 14 | 15 | const decode = (bytes) => { 16 | const ret = SyncDL.decode(bytes); //metas直接就给解了。。。。 17 | const { metas = [] } = ret; // meta中的payload这里不解析。。。额 18 | const metaRet = []; 19 | metas.forEach((meta) => { 20 | const { ns, payload } = meta; 21 | if (ns === STATIC_META_NAMESPACE.MESSAGE) { 22 | meta.payload = msgBodyDecode(payload); 23 | } 24 | if (ns === STATIC_META_NAMESPACE.GROUP_NOTICE) { 25 | meta.payload = groupnoticeDecode(payload); 26 | } 27 | if (ns === STATIC_META_NAMESPACE.ROSTER_NOTICE) { 28 | meta.payload = rosternoticeDecode(payload); 29 | } 30 | if (ns === STATIC_META_NAMESPACE.USER_NOTICE) { 31 | meta.payload = usernoticeDecode(payload); 32 | } 33 | if (ns === STATIC_META_NAMESPACE.INFO) { 34 | meta.payload = infoDecode(payload); 35 | } 36 | if (ns === STATIC_META_NAMESPACE.CONVERSATION) { 37 | meta.payload = convDecode(payload); 38 | } 39 | if (ns === STATIC_META_NAMESPACE.PUSH) { 40 | meta.payload = msgBodyDecode(payload); 41 | } 42 | if (ns === STATIC_META_NAMESPACE.RTC_SIGNAL) { 43 | meta.payload = rtcSignalDecode(payload); 44 | } 45 | metaRet.push(meta); 46 | }); 47 | ret.metas = metaRet; 48 | return ret; 49 | }; 50 | 51 | const encode = (obj) => { 52 | const arr = []; 53 | const { metas = [] } = obj; 54 | metas.forEach((item) => { 55 | arr.push(metaEncode(item)); 56 | }); 57 | obj.metas = arr; 58 | return SyncDL.encode(obj).finish(); 59 | }; 60 | 61 | export { decode, encode }; 62 | -------------------------------------------------------------------------------- /src/sdk/core/protocol/class/syncul.js: -------------------------------------------------------------------------------- 1 | import protobuf from 'protobufjs/light'; 2 | import jsonDescriptor from '../xsync'; 3 | import { constr as metaConstr } from './meta'; 4 | 5 | var root = protobuf.Root.fromJSON(jsonDescriptor); 6 | const SyncUL = root.lookupType('im.floo.protobuf.SyncUL'); 7 | 8 | const decode = (bytes) => { 9 | return SyncUL.decode(bytes); 10 | }; 11 | 12 | const encode = (obj) => { 13 | obj.meta && (obj.meta = metaConstr(obj.meta)); 14 | const ret = SyncUL.encode(obj).finish(); 15 | return ret; 16 | }; 17 | 18 | export { decode, encode }; 19 | -------------------------------------------------------------------------------- /src/sdk/core/protocol/class/unreaddl.js: -------------------------------------------------------------------------------- 1 | import protobuf from 'protobufjs/light'; 2 | import jsonDescriptor from '../xsync'; 3 | 4 | var root = protobuf.Root.fromJSON(jsonDescriptor); 5 | const UnreadDL = root.lookupType('im.floo.protobuf.UnreadDL'); 6 | 7 | const decode = (bytes) => { 8 | const ret = UnreadDL.decode(bytes); 9 | return ret; 10 | }; 11 | 12 | const encode = (obj) => { 13 | return UnreadDL.encode(obj).finish(); 14 | }; 15 | 16 | export { decode, encode }; 17 | -------------------------------------------------------------------------------- /src/sdk/core/protocol/class/usernotice.js: -------------------------------------------------------------------------------- 1 | import protobuf from 'protobufjs/light'; 2 | import jsonDescriptor from '../usernotice'; 3 | 4 | var root = protobuf.Root.fromJSON(jsonDescriptor); 5 | const UserNotice = root.lookupType('im.floo.protobuf.UserNotice'); 6 | 7 | const decode = (bytes) => UserNotice.decode(bytes); 8 | 9 | const encode = (obj) => UserNotice.encode(obj).finish(); 10 | 11 | export { decode, encode }; 12 | -------------------------------------------------------------------------------- /src/sdk/core/protocol/class/xid.js: -------------------------------------------------------------------------------- 1 | import protobuf from 'protobufjs/light'; 2 | import jsonDescriptor from '../xsync'; 3 | 4 | var root = protobuf.Root.fromJSON(jsonDescriptor); 5 | const XID = root.lookupType('im.floo.protobuf.XID'); 6 | 7 | const decode = (bytes) => XID.decode(bytes); 8 | 9 | const encode = (obj) => { 10 | const ret = XID.encode(obj).finish(); 11 | return ret; 12 | }; 13 | 14 | const constr = (obj) => XID.create(obj); 15 | 16 | export { decode, encode, constr }; 17 | -------------------------------------------------------------------------------- /src/sdk/core/protocol/conversation.js: -------------------------------------------------------------------------------- 1 | /*eslint-disable block-scoped-var, id-length, no-control-regex, no-magic-numbers, no-prototype-builtins, no-redeclare, no-shadow, no-var, sort-vars*/ 2 | (function (global, factory) { 3 | /* global define, require, module */ 4 | 5 | /* AMD */ if (typeof define === 'function' && define.amd) define(['protobufjs/light'], factory); 6 | /* CommonJS */ else if (typeof require === 'function' && typeof module === 'object' && module && module.exports) module.exports = factory(require('protobufjs/light')); 7 | })(this, function ($protobuf) { 8 | 'use strict'; 9 | 10 | const $root = ($protobuf.roots.conversation || ($protobuf.roots.conversation = new $protobuf.Root())).addJSON({ 11 | im: { 12 | nested: { 13 | floo: { 14 | nested: { 15 | protobuf: { 16 | nested: { 17 | Conversation: { 18 | fields: { 19 | type: { 20 | type: 'Type', 21 | id: 1 22 | }, 23 | operation: { 24 | type: 'ConversationOperation', 25 | id: 2 26 | } 27 | }, 28 | nested: { 29 | Type: { 30 | values: { 31 | UNKNOWN: 0, 32 | OPER: 1 33 | } 34 | } 35 | } 36 | }, 37 | ConversationOperation: { 38 | fields: { 39 | type: { 40 | type: 'OpType', 41 | id: 1 42 | }, 43 | xid: { 44 | type: 'XID', 45 | id: 2 46 | } 47 | }, 48 | nested: { 49 | OpType: { 50 | values: { 51 | UNKNOWN: 0, 52 | DELETE: 1, 53 | DELETE_EVERYWHERE: 2 54 | } 55 | } 56 | } 57 | }, 58 | XID: { 59 | fields: { 60 | uid: { 61 | type: 'uint64', 62 | id: 1 63 | }, 64 | deviceSN: { 65 | type: 'uint32', 66 | id: 2 67 | } 68 | } 69 | } 70 | } 71 | } 72 | } 73 | } 74 | } 75 | } 76 | }); 77 | 78 | return $root; 79 | }); 80 | -------------------------------------------------------------------------------- /src/sdk/core/protocol/groupnotice.js: -------------------------------------------------------------------------------- 1 | /*eslint-disable block-scoped-var, id-length, no-control-regex, no-magic-numbers, no-prototype-builtins, no-redeclare, no-shadow, no-var, sort-vars*/ 2 | (function (global, factory) { 3 | /* global define, require, module */ 4 | 5 | /* AMD */ if (typeof define === 'function' && define.amd) define(['protobufjs/light'], factory); 6 | /* CommonJS */ else if (typeof require === 'function' && typeof module === 'object' && module && module.exports) module.exports = factory(require('protobufjs/light')); 7 | })(this, function ($protobuf) { 8 | 'use strict'; 9 | 10 | const $root = ($protobuf.roots.groupnotice || ($protobuf.roots.groupnotice = new $protobuf.Root())).addJSON({ 11 | im: { 12 | nested: { 13 | floo: { 14 | nested: { 15 | protobuf: { 16 | nested: { 17 | GroupNotice: { 18 | fields: { 19 | type: { 20 | type: 'Type', 21 | id: 1 22 | }, 23 | gid: { 24 | type: 'XID', 25 | id: 2 26 | }, 27 | from: { 28 | type: 'XID', 29 | id: 3 30 | }, 31 | to: { 32 | rule: 'repeated', 33 | type: 'XID', 34 | id: 4 35 | }, 36 | content: { 37 | type: 'string', 38 | id: 5 39 | } 40 | }, 41 | nested: { 42 | Type: { 43 | values: { 44 | UNKNOWN: 0, 45 | PRESENCE: 1, 46 | ABSENCE: 2, 47 | CREATED: 3, 48 | DESTROYED: 4, 49 | JOINED: 5, 50 | LEAVED: 6, 51 | APPLYED: 7, 52 | APPLY_ACCEPTED: 8, 53 | APPLY_DECLINED: 9, 54 | INVITED: 10, 55 | INVITE_ACCEPTED: 11, 56 | INVITE_DECLINED: 12, 57 | KICKED: 13, 58 | BLOCKED: 14, 59 | UNBLOCKED: 15, 60 | OWNER_ASSIGNED: 16, 61 | ADMIN_GRANTED: 17, 62 | ADMIN_REVOKED: 18, 63 | MUTED: 19, 64 | UNMUTED: 20, 65 | BANNED: 21, 66 | UNBANNED: 22, 67 | INFO_UPDATED: 23, 68 | ANNOUNCEMENT_UPDATED: 24, 69 | MESSAGE_SETTING: 25, 70 | FILE_UPLOADED: 26, 71 | FILE_DELETED: 27, 72 | FILE_UPDATED: 28 73 | } 74 | } 75 | } 76 | }, 77 | XID: { 78 | fields: { 79 | uid: { 80 | type: 'uint64', 81 | id: 1 82 | }, 83 | deviceSN: { 84 | type: 'uint32', 85 | id: 2 86 | } 87 | } 88 | } 89 | } 90 | } 91 | } 92 | } 93 | } 94 | } 95 | }); 96 | 97 | return $root; 98 | }); 99 | -------------------------------------------------------------------------------- /src/sdk/core/protocol/index.js: -------------------------------------------------------------------------------- 1 | import { frameEncode, packProvision, packSyncul, packUnreadul, unpack } from './class/index'; 2 | export { packSyncul, packProvision, unpack, packUnreadul, frameEncode }; 3 | -------------------------------------------------------------------------------- /src/sdk/core/protocol/info.js: -------------------------------------------------------------------------------- 1 | /*eslint-disable block-scoped-var, id-length, no-control-regex, no-magic-numbers, no-prototype-builtins, no-redeclare, no-shadow, no-var, sort-vars*/ 2 | (function (global, factory) { 3 | /* global define, require, module */ 4 | 5 | /* AMD */ if (typeof define === 'function' && define.amd) define(['protobufjs/light'], factory); 6 | /* CommonJS */ else if (typeof require === 'function' && typeof module === 'object' && module && module.exports) module.exports = factory(require('protobufjs/light')); 7 | })(this, function ($protobuf) { 8 | 'use strict'; 9 | 10 | const $root = ($protobuf.roots.info || ($protobuf.roots.info = new $protobuf.Root())).addJSON({ 11 | im: { 12 | nested: { 13 | floo: { 14 | nested: { 15 | protobuf: { 16 | nested: { 17 | Info: { 18 | fields: { 19 | sdk_vsn: { 20 | type: 'string', 21 | id: 1 22 | }, 23 | network: { 24 | type: 'Network', 25 | id: 2 26 | }, 27 | content: { 28 | type: 'string', 29 | id: 3 30 | } 31 | }, 32 | nested: { 33 | Network: { 34 | values: { 35 | WIRE: 0, 36 | WIFI: 1, 37 | NET_2G: 2, 38 | NET_3G: 3, 39 | NET_4G: 4, 40 | NET_5G: 5, 41 | UNKNOWN: 6 42 | } 43 | } 44 | } 45 | } 46 | } 47 | } 48 | } 49 | } 50 | } 51 | } 52 | }); 53 | 54 | return $root; 55 | }); 56 | -------------------------------------------------------------------------------- /src/sdk/core/protocol/make.sh: -------------------------------------------------------------------------------- 1 | # /etc/bash/bashrc 2 | 3 | # https://www.npmjs.com/package/protobufjs#pbjs-for-javascript 4 | # install pbjs by: 5 | # yarn global add protobufjs 6 | 7 | echo "====== deleting old files =======" 8 | find . -path ./class -prune -false -o -name "*.js" ! -name "index.js" -exec rm {} \; 9 | 10 | echo "==== starting making pb files ..." 11 | xsync_dir=$1 12 | for proto_file in `find $xsync_dir -type f -name "*.proto"`; 13 | do 14 | proto_filename=`basename ${proto_file}` 15 | proto_name=${proto_filename%%.proto} 16 | js_file=${proto_name}.js 17 | echo "generate ${js_file} from ${proto_file}" 18 | pbjs -t json-module ${proto_file} -o ${js_file} -r ${proto_name} --keep-case --es6 19 | done 20 | 21 | echo "==== pb file make over......" 22 | -------------------------------------------------------------------------------- /src/sdk/core/protocol/rosternotice.js: -------------------------------------------------------------------------------- 1 | /*eslint-disable block-scoped-var, id-length, no-control-regex, no-magic-numbers, no-prototype-builtins, no-redeclare, no-shadow, no-var, sort-vars*/ 2 | (function (global, factory) { 3 | /* global define, require, module */ 4 | 5 | /* AMD */ if (typeof define === 'function' && define.amd) define(['protobufjs/light'], factory); 6 | /* CommonJS */ else if (typeof require === 'function' && typeof module === 'object' && module && module.exports) module.exports = factory(require('protobufjs/light')); 7 | })(this, function ($protobuf) { 8 | 'use strict'; 9 | 10 | const $root = ($protobuf.roots.rosternotice || ($protobuf.roots.rosternotice = new $protobuf.Root())).addJSON({ 11 | im: { 12 | nested: { 13 | floo: { 14 | nested: { 15 | protobuf: { 16 | nested: { 17 | RosterNotice: { 18 | fields: { 19 | type: { 20 | type: 'Type', 21 | id: 1 22 | }, 23 | from: { 24 | type: 'XID', 25 | id: 2 26 | }, 27 | to: { 28 | rule: 'repeated', 29 | type: 'XID', 30 | id: 3 31 | }, 32 | content: { 33 | type: 'string', 34 | id: 4 35 | }, 36 | roster_vsn: { 37 | type: 'string', 38 | id: 5 39 | } 40 | }, 41 | nested: { 42 | Type: { 43 | values: { 44 | UNKNOWN: 0, 45 | ADDED: 1, 46 | REMOVED: 2, 47 | ACCEPTED: 3, 48 | DECLINED: 4, 49 | BLOCKED: 5, 50 | UNBLOCKED: 6, 51 | APPLIED: 7, 52 | INFO_UPDATED: 8, 53 | MUTED: 9, 54 | UNMUTED: 10 55 | } 56 | } 57 | } 58 | }, 59 | XID: { 60 | fields: { 61 | uid: { 62 | type: 'uint64', 63 | id: 1 64 | }, 65 | deviceSN: { 66 | type: 'uint32', 67 | id: 2 68 | } 69 | } 70 | } 71 | } 72 | } 73 | } 74 | } 75 | } 76 | } 77 | }); 78 | 79 | return $root; 80 | }); 81 | -------------------------------------------------------------------------------- /src/sdk/core/protocol/rtcsignal.js: -------------------------------------------------------------------------------- 1 | /*eslint-disable block-scoped-var, id-length, no-control-regex, no-magic-numbers, no-prototype-builtins, no-redeclare, no-shadow, no-var, sort-vars*/ 2 | (function (global, factory) { 3 | /* global define, require, module */ 4 | 5 | /* AMD */ if (typeof define === 'function' && define.amd) define(['protobufjs/light'], factory); 6 | /* CommonJS */ else if (typeof require === 'function' && typeof module === 'object' && module && module.exports) module.exports = factory(require('protobufjs/light')); 7 | })(this, function ($protobuf) { 8 | 'use strict'; 9 | 10 | const $root = ($protobuf.roots.rtcsignal || ($protobuf.roots.rtcsignal = new $protobuf.Root())).addJSON({ 11 | im: { 12 | nested: { 13 | floo: { 14 | nested: { 15 | protobuf: { 16 | nested: { 17 | RtcSignal: { 18 | fields: { 19 | type: { 20 | type: 'Type', 21 | id: 1 22 | }, 23 | content: { 24 | type: 'string', 25 | id: 2 26 | }, 27 | media_server: { 28 | type: 'string', 29 | id: 3 30 | } 31 | }, 32 | nested: { 33 | Type: { 34 | values: { 35 | UNKNOWN: 0, 36 | VIDEO_ROOM: 1 37 | } 38 | } 39 | } 40 | } 41 | } 42 | } 43 | } 44 | } 45 | } 46 | } 47 | }); 48 | 49 | return $root; 50 | }); 51 | -------------------------------------------------------------------------------- /src/sdk/core/protocol/usernotice.js: -------------------------------------------------------------------------------- 1 | /*eslint-disable block-scoped-var, id-length, no-control-regex, no-magic-numbers, no-prototype-builtins, no-redeclare, no-shadow, no-var, sort-vars*/ 2 | (function (global, factory) { 3 | /* global define, require, module */ 4 | 5 | /* AMD */ if (typeof define === 'function' && define.amd) define(['protobufjs/light'], factory); 6 | /* CommonJS */ else if (typeof require === 'function' && typeof module === 'object' && module && module.exports) module.exports = factory(require('protobufjs/light')); 7 | })(this, function ($protobuf) { 8 | 'use strict'; 9 | 10 | const $root = ($protobuf.roots.usernotice || ($protobuf.roots.usernotice = new $protobuf.Root())).addJSON({ 11 | im: { 12 | nested: { 13 | floo: { 14 | nested: { 15 | protobuf: { 16 | nested: { 17 | UserNotice: { 18 | fields: { 19 | type: { 20 | type: 'Type', 21 | id: 1 22 | }, 23 | content: { 24 | type: 'string', 25 | id: 2 26 | } 27 | }, 28 | nested: { 29 | Type: { 30 | values: { 31 | UNKNOWN: 0, 32 | PASSWORD_CHANGED: 1, 33 | FROZEN: 2, 34 | REMOVED: 3, 35 | KICK_BY_SAME_DEVICE: 4, 36 | KICKED_BY_OTHER_DEVICE: 5, 37 | INFO_UPDATED: 6, 38 | DEVICE_LOGIN: 7, 39 | DEVICE_LOGOUT: 8, 40 | DEVICE_ADDED: 9, 41 | DEVICE_REMOVED: 10, 42 | CLUSTER_CHANGED: 11, 43 | DNS_UPDATE: 12, 44 | TRAFFIC_LIMIT_EXCEEDED: 13 45 | } 46 | } 47 | } 48 | } 49 | } 50 | } 51 | } 52 | } 53 | } 54 | } 55 | }); 56 | 57 | return $root; 58 | }); 59 | -------------------------------------------------------------------------------- /src/sdk/core/protocol/xid.js: -------------------------------------------------------------------------------- 1 | /*eslint-disable block-scoped-var, id-length, no-control-regex, no-magic-numbers, no-prototype-builtins, no-redeclare, no-shadow, no-var, sort-vars*/ 2 | (function (global, factory) { 3 | /* global define, require, module */ 4 | 5 | /* AMD */ if (typeof define === 'function' && define.amd) define(['protobufjs/light'], factory); 6 | /* CommonJS */ else if (typeof require === 'function' && typeof module === 'object' && module && module.exports) module.exports = factory(require('protobufjs/light')); 7 | })(this, function ($protobuf) { 8 | 'use strict'; 9 | 10 | const $root = ($protobuf.roots.xid || ($protobuf.roots.xid = new $protobuf.Root())).addJSON({ 11 | im: { 12 | nested: { 13 | floo: { 14 | nested: { 15 | protobuf: { 16 | nested: { 17 | XID: { 18 | fields: { 19 | uid: { 20 | type: 'uint64', 21 | id: 1 22 | }, 23 | deviceSN: { 24 | type: 'uint32', 25 | id: 2 26 | } 27 | } 28 | } 29 | } 30 | } 31 | } 32 | } 33 | } 34 | } 35 | }); 36 | 37 | return $root; 38 | }); 39 | -------------------------------------------------------------------------------- /src/sdk/index.js: -------------------------------------------------------------------------------- 1 | import rosterManage from './manage/rosterManage'; 2 | import groupManage from './manage/groupManage'; 3 | import userManage from './manage/userManage'; 4 | import sysManage from './manage/sysManage'; 5 | import rtcManage from './manage/rtcManager'; 6 | import webim from './core/base/index'; 7 | 8 | webim.rosterManage = rosterManage; 9 | webim.groupManage = groupManage; 10 | webim.userManage = userManage; 11 | webim.sysManage = sysManage; 12 | webim.rtcManage = rtcManage; 13 | 14 | export function flooim(config) { 15 | new webim(config); 16 | return webim; 17 | } 18 | 19 | export default flooim; 20 | 21 | window.flooIM = (config) => flooim(config); 22 | -------------------------------------------------------------------------------- /src/sdk/model/conversation.js: -------------------------------------------------------------------------------- 1 | function conversation(params) { 2 | typeof params.type !== 'undefined' && (this.type = params.type); 3 | typeof params.operation !== 'undefined' && (this.operation = params.operation); 4 | } 5 | 6 | conversation.prototype = { 7 | setType: function (type) { 8 | this.type = type; 9 | }, 10 | setOperation: function (operation) { 11 | this.operation = operation; 12 | } 13 | }; 14 | 15 | export default conversation; 16 | -------------------------------------------------------------------------------- /src/sdk/model/frame.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | VSN vsn = 1; 4 | CompressMethod compress_method = 2; 5 | Command command = 3; 6 | bytes payload = 4; 7 | 8 | */ 9 | import { STATIC_FRAME_COMPRESS_METHOD, STATIC_FRAME_VSN } from '../utils/static'; 10 | 11 | function frame(params) { 12 | params = Object.assign( 13 | {}, 14 | { 15 | vsn: STATIC_FRAME_VSN.XSYNC_V1, 16 | compress_method: STATIC_FRAME_COMPRESS_METHOD.NONE 17 | }, 18 | params 19 | ); 20 | typeof params.vsn !== 'undefined' && (this.vsn = params.vsn); 21 | typeof params.compress_method !== 'undefined' && (this.compress_method = params.compress_method); 22 | typeof params.command !== 'undefined' && (this.command = params.command); 23 | typeof params.payload !== 'undefined' && (this.payload = params.payload); 24 | typeof params.encrypt_method !== 'undefined' && (this.encrypt_method = params.encrypt_method); 25 | typeof params.encrypt_key !== 'undefined' && (this.encrypt_key = params.encrypt_key); 26 | typeof params.check_sum !== 'undefined' && (this.check_sum = params.check_sum); 27 | typeof params.tag !== 'undefined' && (this.tag = params.tag); 28 | } 29 | 30 | frame.prototype = { 31 | setVsn: function (vsn) { 32 | this.vsn = vsn; 33 | }, 34 | setCompressmethod: function (compress_method) { 35 | this.compress_method = compress_method; 36 | }, 37 | setCommond: function (command) { 38 | this.command = command; 39 | }, 40 | setPayload: function (payload) { 41 | this.payload = payload; 42 | }, 43 | setEncryptmethod: function (encrypt_method) { 44 | this.encrypt_method = encrypt_method; 45 | }, 46 | setEncryptkey: function (encrypt_key) { 47 | this.encrypt_key = encrypt_key; 48 | }, 49 | setChecksum: function (check_sum) { 50 | this.check_sum = check_sum; 51 | }, 52 | setTag: function (tag) { 53 | this.tag = tag; 54 | } 55 | }; 56 | 57 | export default frame; 58 | -------------------------------------------------------------------------------- /src/sdk/model/index.js: -------------------------------------------------------------------------------- 1 | import xid from './xid'; 2 | import message from './message'; 3 | import frame from './frame'; 4 | import provision from './provision'; 5 | import syncdl from './syncdl'; 6 | import syncul from './syncul'; 7 | import meta from './meta'; 8 | import conversation from './conversation'; 9 | import rtcmessage from './rtcmessage'; 10 | 11 | export { xid, message, frame, provision, syncdl, syncul, meta, conversation, rtcmessage }; 12 | -------------------------------------------------------------------------------- /src/sdk/model/message.js: -------------------------------------------------------------------------------- 1 | /** 2 | Type type = 1; 3 | XID from = 2; 4 | XID to = 3; 5 | string content = 4; 6 | ContentType ctype = 5; 7 | MessageOperation operation = 6; 8 | string config = 7; 9 | string attachment = 8; 10 | string ext = 9; 11 | QoS qos = 10; 12 | string sender_name = 11; 13 | bool is_system = 12; // system message 14 | uint32 priority = 13; 15 | Status status = 14; 16 | uint64 edit_timestamp = 15; 17 | */ 18 | // import xid from './xid'; 19 | import { STATIC_MESSAGE_CONTENT_TYPE, STATIC_MESSAGE_OPTYPE, STATIC_MESSAGE_TYPE } from '../utils/static'; 20 | 21 | function message(params) { 22 | const { 23 | operation = { 24 | type: STATIC_MESSAGE_OPTYPE.UNKNOWN, 25 | mid: 0 26 | } 27 | } = params; 28 | params = Object.assign( 29 | {}, 30 | { 31 | type: STATIC_MESSAGE_TYPE.NORMAL, 32 | ctype: STATIC_MESSAGE_CONTENT_TYPE.TEXT, 33 | content: '', 34 | operation 35 | }, 36 | params 37 | ); 38 | typeof params.type !== 'undefined' && (this.type = params.type); 39 | typeof params.from !== 'undefined' && (this.from = params.from); 40 | typeof params.to !== 'undefined' && (this.to = params.to); 41 | typeof params.content !== 'undefined' && (this.content = params.content); 42 | typeof params.ctype !== 'undefined' && (this.ctype = params.ctype); 43 | typeof params.operation !== 'undefined' && (this.operation = params.operation); 44 | typeof params.config !== 'undefined' && (this.config = params.config); 45 | typeof params.attachment !== 'undefined' && (this.attachment = params.attachment); 46 | typeof params.ext !== 'undefined' && (this.ext = params.ext); 47 | typeof params.qos !== 'undefined' && (this.qos = params.qos); 48 | typeof params.sender_name !== 'undefined' && (this.sender_name = params.sender_name); 49 | typeof params.is_system != 'undefined' && (this.is_system = params.is_system); 50 | typeof params.priority != 'undefined' && (this.priority = params.priority); 51 | typeof params.status != 'undefined' && (this.status = params.status); 52 | typeof params.edit_timestamp != 'undefined' && (this.edit_timestamp = params.edit_timestamp); 53 | } 54 | 55 | message.prototype = { 56 | setType: function (type) { 57 | this.type = type; 58 | }, 59 | setFrom: function (from) { 60 | this.from = from; 61 | }, 62 | setTo: function (to) { 63 | this.to = to; 64 | }, 65 | setContent: function (content) { 66 | this.content = content; 67 | }, 68 | setCtype: function (ctype) { 69 | this.ctype = ctype; 70 | }, 71 | setOperation: function (operation) { 72 | this.operation = operation; 73 | }, 74 | setConfig: function (config) { 75 | this.config = config; 76 | }, 77 | setAttachment: function (attachment) { 78 | this.attachment = attachment; 79 | }, 80 | setExt: function (ext) { 81 | this.ext = ext; 82 | }, 83 | setQos: function (qos) { 84 | this.qos = qos; 85 | }, 86 | setSendername: function (sender_name) { 87 | this.sender_name = sender_name; 88 | }, 89 | setIssystem: function (is_system) { 90 | this.is_system = is_system; 91 | }, 92 | setPriority: function (priority) { 93 | this.priority = priority; 94 | }, 95 | setStatus: function (status) { 96 | this.status = status; 97 | }, 98 | setEditTimestamp: function (edit_timestamp) { 99 | this.edit_timestamp = edit_timestamp; 100 | } 101 | }; 102 | 103 | export default message; 104 | -------------------------------------------------------------------------------- /src/sdk/model/meta.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | uint64 id = 1; 4 | XID from = 2; 5 | XID to = 3; 6 | uint64 timestamp = 4; 7 | NameSpace ns = 5; 8 | bytes payload = 6; 9 | 10 | */ 11 | // import xid from './xid'; 12 | 13 | function meta(params) { 14 | params = Object.assign({}, params); 15 | typeof params.id !== 'undefined' && (this.id = params.id); 16 | typeof params.from !== 'undefined' && (this.from = params.from); 17 | typeof params.to !== 'undefined' && (this.to = params.to); 18 | typeof params.timestamp !== 'undefined' && (this.timestamp = params.timestamp); 19 | typeof params.ns !== 'undefined' && (this.ns = params.ns); 20 | typeof params.payload !== 'undefined' && (this.payload = params.payload); 21 | } 22 | 23 | meta.prototype = { 24 | setId: function (id) { 25 | this.id = id; 26 | }, 27 | setFrom: function (from) { 28 | this.from = from; 29 | }, 30 | setTo: function (to) { 31 | this.to = to; 32 | }, 33 | setTimestamp: function (timestamp) { 34 | this.timestamp = timestamp; 35 | }, 36 | setNs: function (ns) { 37 | this.ns = ns; 38 | }, 39 | setPayload: function (payload) { 40 | this.payload = payload; 41 | } 42 | }; 43 | 44 | export default meta; 45 | -------------------------------------------------------------------------------- /src/sdk/model/provision.js: -------------------------------------------------------------------------------- 1 | /** 2 | Status status = 1; 3 | XID xid = 2; 4 | EncryptMethod encrypt_method = 3; 5 | string encrypt_key = 4; 6 | string password = 5; 7 | string token = 6; 8 | OsType os_type = 7; 9 | string sdk_vsn = 8; 10 | bool is_manual_login = 9; 11 | string device_guid = 10; // Device Global Unique Id 12 | string device_notifier = 11; // offline msg push use this 13 | string device_token = 12; 14 | string device_info = 13; // User Agent or others 15 | uint64 last_login_time = 14; // last login time in millisecond 16 | */ 17 | // import xid from './xid'; 18 | import { STATIC_FRAME_ENCRYPTMETHOD, STATIC_FRAME_OSTYPE } from '../utils/static'; 19 | 20 | function provision(params) { 21 | params = Object.assign( 22 | {}, 23 | { 24 | encrypt_method: STATIC_FRAME_ENCRYPTMETHOD.ENCRYPT_NONE, 25 | os_type: STATIC_FRAME_OSTYPE.WEB 26 | // device_guid: infoStore.getDeviceGuid(), 27 | }, 28 | params 29 | ); 30 | typeof params.status !== 'undefined' && (this.status = params.status); 31 | typeof params.xid !== 'undefined' && (this.xid = params.xid); 32 | typeof params.encrypt_method !== 'undefined' && (this.encrypt_method = params.encrypt_method); 33 | typeof params.encrypt_key !== 'undefined' && (this.encrypt_key = params.encrypt_key); 34 | typeof params.password !== 'undefined' && (this.password = params.password); 35 | typeof params.token !== 'undefined' && (this.token = params.token); 36 | typeof params.os_type !== 'undefined' && (this.os_type = params.os_type); 37 | typeof params.sdk_vsn !== 'undefined' && (this.sdk_vsn = params.sdk_vsn); 38 | typeof params.is_manual_login !== 'undefined' && (this.is_manual_login = params.is_manual_login); 39 | typeof params.device_guid !== 'undefined' && (this.device_guid = params.device_guid); 40 | typeof params.device_notifier !== 'undefined' && (this.device_notifier = params.device_notifier); 41 | typeof params.device_token !== 'undefined' && (this.device_token = params.device_token); 42 | typeof params.device_info !== 'undefined' && (this.device_info = params.device_info); 43 | typeof params.last_login_time !== 'undefined' && (this.last_login_time = params.last_login_time); 44 | } 45 | 46 | provision.prototype = { 47 | setStatus: function (status) { 48 | this.status = status; 49 | }, 50 | setXid: function (xid) { 51 | this.xid = xid; 52 | }, 53 | setEncryptmethod: function (encrypt_method) { 54 | this.encrypt_method = encrypt_method; 55 | }, 56 | setEncryptkey: function (encrypt_key) { 57 | this.encrypt_key = encrypt_key; 58 | }, 59 | setPassword: function (password) { 60 | this.password = password; 61 | }, 62 | setToken: function (token) { 63 | this.token = token; 64 | }, 65 | setOstype: function (os_type) { 66 | this.os_type = os_type; 67 | }, 68 | setSdkvsn: function (sdk_vsn) { 69 | this.sdk_vsn = sdk_vsn; 70 | }, 71 | setIsmanuallogin: function (is_manual_login) { 72 | this.is_manual_login = is_manual_login; 73 | }, 74 | setDeviceguid: function (device_guid) { 75 | this.device_guid = device_guid; 76 | }, 77 | setDevicenotifier: function (device_notifier) { 78 | this.device_notifier = device_notifier; 79 | }, 80 | setDevicetoken: function (device_token) { 81 | this.device_token = device_token; 82 | }, 83 | setDeviceinfo: function (device_info) { 84 | this.device_info = device_info; 85 | }, 86 | setLastlogintime: function (last_login_time) { 87 | this.last_login_time = last_login_time; 88 | } 89 | }; 90 | 91 | export default provision; 92 | -------------------------------------------------------------------------------- /src/sdk/model/rtcmessage.js: -------------------------------------------------------------------------------- 1 | /* 2 | Type type = 1; 3 | string content = 2; 4 | string media_server = 3; 5 | **/ 6 | import { STATIC_RTC_SIGNAL_TYPE } from '../utils/static'; 7 | 8 | function rtcmessage(params) { 9 | params = Object.assign( 10 | {}, 11 | { 12 | type: STATIC_RTC_SIGNAL_TYPE.VIDEO_ROOM, 13 | content: '' 14 | }, 15 | params 16 | ); 17 | typeof params.type !== 'undefined' && (this.type = params.type); 18 | typeof params.content !== 'undefined' && (this.content = params.content); 19 | typeof params.media_server !== 'undefined' && (this.media_server = params.media_server); 20 | } 21 | 22 | rtcmessage.prototype = { 23 | setType: function (type) { 24 | this.type = type; 25 | }, 26 | setContent: function (content) { 27 | this.content = content; 28 | }, 29 | setMediaServer: function (media_server) { 30 | this.media_server = media_server; 31 | } 32 | }; 33 | 34 | export default rtcmessage; 35 | -------------------------------------------------------------------------------- /src/sdk/model/syncdl.js: -------------------------------------------------------------------------------- 1 | /*** 2 | Status status = 1; 3 | repeated Meta metas = 2; 4 | uint64 next_key = 3; 5 | XID xid = 4; 6 | uint64 client_mid = 5; 7 | uint64 server_mid = 6; 8 | uint64 server_time = 7; 9 | bool is_full_sync = 8; 10 | uint64 prev_mid = 9; 11 | bool is_eager_sync = 10; 12 | uint64 edit_timestamp = 11; 13 | */ 14 | function syncdl(params) { 15 | params = Object.assign({}, params); 16 | typeof params.status !== 'undefined' && (this.status = params.status); 17 | typeof params.metas !== 'undefined' && (this.metas = params.metas); 18 | typeof params.next_key !== 'undefined' && (this.next_key = params.next_key); 19 | typeof params.xid !== 'undefined' && (this.xid = params.xid); 20 | typeof params.client_mid !== 'undefined' && (this.client_mid = params.client_mid); 21 | typeof params.server_mid !== 'undefined' && (this.server_mid = params.server_mid); 22 | typeof params.server_time !== 'undefined' && (this.server_time = params.server_time); 23 | typeof params.is_full_sync !== 'undefined' && (this.is_full_sync = params.is_full_sync); 24 | typeof params.prev_mid !== 'undefined' && (this.prev_mid = params.prev_mid); 25 | typeof params.is_eager_sync !== 'undefined' && (this.is_eager_sync = params.is_eager_sync); 26 | typeof params.edit_timestamp !== 'undefined' && (this.edit_timestamp = params.edit_timestamp); 27 | } 28 | 29 | syncdl.prototype = { 30 | setStatus: function (status) { 31 | this.status = status; 32 | }, 33 | setMetas: function (metas) { 34 | this.metas = metas; 35 | }, 36 | setNextkey: function (next_key) { 37 | this.next_key = next_key; 38 | }, 39 | setXid: function (xid) { 40 | this.xid = xid; 41 | }, 42 | setClientmid: function (client_mid) { 43 | this.client_mid = client_mid; 44 | }, 45 | setServermid: function (server_mid) { 46 | this.server_mid = server_mid; 47 | }, 48 | setServertime: function (server_time) { 49 | this.server_time = server_time; 50 | }, 51 | setIsfullsync: function (is_full_sync) { 52 | this.is_full_sync = is_full_sync; 53 | }, 54 | setPrevmid: function (prev_mid) { 55 | this.prev_mid = prev_mid; 56 | }, 57 | setIseagersync: function (is_eager_sync) { 58 | this.is_eager_sync = is_eager_sync; 59 | }, 60 | setEditTimestamp: function (edit_timestamp) { 61 | this.edit_timestamp = edit_timestamp; 62 | } 63 | }; 64 | 65 | export default syncdl; 66 | -------------------------------------------------------------------------------- /src/sdk/model/syncul.js: -------------------------------------------------------------------------------- 1 | /*** 2 | * 3 | XID xid = 1; 4 | uint64 key = 2; 5 | Meta meta = 3; // for send message 6 | bool is_full_sync = 4; 7 | uint32 full_sync_num = 5; 8 | 9 | */ 10 | function syncul(params) { 11 | params = Object.assign({}, params); 12 | typeof params.xid !== 'undefined' && (this.xid = params.xid); 13 | typeof params.key !== 'undefined' && (this.key = params.key); 14 | typeof params.meta !== 'undefined' && (this.meta = params.meta); 15 | typeof params.is_full_sync !== 'undefined' && (this.is_full_sync = params.is_full_sync); 16 | typeof params.full_sync_num !== 'undefined' && (this.full_sync_num = params.full_sync_num); 17 | } 18 | 19 | syncul.prototype = { 20 | setXid: function (xid) { 21 | this.xid = xid; 22 | }, 23 | setKey: function (key) { 24 | this.key = key; 25 | }, 26 | setMeta: function (meta) { 27 | this.meta = meta; 28 | }, 29 | setIsfullsync: function (is_full_sync) { 30 | this.is_full_sync = is_full_sync; 31 | }, 32 | setFullsyncnum: function (full_sync_num) { 33 | this.full_sync_num = full_sync_num; 34 | } 35 | }; 36 | 37 | export default syncul; 38 | -------------------------------------------------------------------------------- /src/sdk/model/xid.js: -------------------------------------------------------------------------------- 1 | /* 2 | uint64 uid = 1; // or gid 3 | uint32 deviceSN = 2; // device serial number 4 | **/ 5 | import { infoStore } from '../utils/store'; 6 | 7 | const xid = function (params = {}) { 8 | this.uid = params.uid || 0; 9 | const deviceSN = typeof params.deviceSN === 'undefined' ? infoStore.getDeviceSN() : params.deviceSN; 10 | this.deviceSN = deviceSN; 11 | }; 12 | 13 | xid.prototype.setUid = function (uid) { 14 | this.uid = uid; 15 | }; 16 | 17 | xid.prototype.setDeviceSN = function (sn) { 18 | this.deviceSN = sn; 19 | }; 20 | 21 | export default xid; 22 | -------------------------------------------------------------------------------- /src/sdk/utils/cusEvent.js: -------------------------------------------------------------------------------- 1 | const __events = {}; 2 | 3 | const __judgeEvent = (name) => { 4 | if (typeof __events[name] === 'undefined') { 5 | __events[name] = []; 6 | } 7 | return __events[name]; 8 | }; 9 | 10 | const bind = (name, func) => { 11 | const judge = __judgeEvent(name); 12 | const idx = judge.findIndex((f) => f.toString() === func.toString()); 13 | if (idx > -1) { 14 | __judgeEvent(name).splice(idx, 1); 15 | } 16 | __judgeEvent(name).push(func); 17 | }; 18 | 19 | const unBind = (name, func) => { 20 | const index = __judgeEvent(name).findIndex((item) => item.toString() === func.toString()); 21 | if (index >= 0) { 22 | __events[name].splice(index, 1); 23 | } 24 | }; 25 | 26 | const unBindAll = (name) => { 27 | __events[name] = null; 28 | }; 29 | 30 | const fire = (name, param) => { 31 | __judgeEvent(name).forEach((func) => { 32 | func(param); 33 | }); 34 | }; 35 | 36 | export { bind, unBind, unBindAll, fire }; 37 | -------------------------------------------------------------------------------- /src/sdk/utils/encrypt.js: -------------------------------------------------------------------------------- 1 | import CryptoJS from 'crypto-js'; 2 | 3 | function mapKey(key) { 4 | const seed = Number(toUint8(key).join('')); 5 | const dict = new Array(256); 6 | 7 | for (let i = 0; i < 256; i++) { 8 | const temp = dict[i] || i; 9 | const rand = ((seed % (i + 1)) + i) % 256; 10 | dict[i] = dict[rand] || rand; 11 | dict[rand] = temp; 12 | } 13 | 14 | return dict; 15 | } 16 | 17 | function toUint8(str) { 18 | return str 19 | .toString() 20 | .split('') 21 | .map((char) => char.charCodeAt(0)); 22 | } 23 | 24 | function byteIn(keyMap, val, index) { 25 | for (let i = 0; i < keyMap.length; i++) { 26 | if (keyMap[i] === val) return (i + keyMap[index]) % 256; 27 | } 28 | } 29 | 30 | function byteOut(keyMap, val, index) { 31 | const diff = val - keyMap[index]; 32 | return keyMap[diff < 0 ? 256 + diff : diff]; 33 | } 34 | 35 | function encrypt(bytes, key) { 36 | if (typeof bytes === 'string') bytes = toUint8(bytes); 37 | return bytes.map(byteIn.bind(null, mapKey(String(key)))); 38 | } 39 | 40 | function decrypt(bytes, key) { 41 | return bytes.map(byteOut.bind(null, mapKey(String(key)))); 42 | } 43 | 44 | const cryptoEncrypt = (str, key) => { 45 | return CryptoJS.AES.encrypt(str, key); 46 | }; 47 | 48 | const cryptoDecript = (str, key) => { 49 | return CryptoJS.AES.decrypt(str, key); 50 | }; 51 | 52 | // tools from wasm 53 | /* 54 | function Encrypt (plainText, key, iv) { 55 | var key = CryptoJS.enc.Utf8.parse(key); 56 | var iv = CryptoJS.enc.Utf8.parse(iv); 57 | var encrypted = CryptoJS.AES.encrypt(plainText, key, { 58 | iv: iv, 59 | mode: CryptoJS.mode.CBC, 60 | padding: CryptoJS.pad.Pkcs7 61 | }); 62 | return encrypted.toString(); //返回的是base64格式的密文 63 | } 64 | 65 | function Decrypt (encryptedText, key, iv) { 66 | var key = CryptoJS.enc.Utf8.parse(key); 67 | var iv = CryptoJS.enc.Utf8.parse(iv); 68 | var decrypted = CryptoJS.AES.decrypt(encryptedText, key, { 69 | iv: iv, 70 | mode: CryptoJS.mode.CBC, 71 | padding: CryptoJS.pad.Pkcs7 72 | }); 73 | const ret = decrypted.toString(CryptoJS.enc.Utf8); 74 | return ret; 75 | } 76 | */ 77 | 78 | export { encrypt, decrypt, cryptoEncrypt, cryptoDecript }; 79 | -------------------------------------------------------------------------------- /src/sdk/utils/log.js: -------------------------------------------------------------------------------- 1 | const isProduction = process.env.NODE_ENV === 'production'; 2 | var logLevel = 'debug'; 3 | const errList = []; 4 | const empty = () => {}; 5 | const requestLog = (info) => { 6 | errList.push(info); 7 | if (errList.length > 5) { 8 | errList.splice(-5); 9 | } 10 | document.querySelector('#slog').innerHTML = errList.join('
'); 11 | }; 12 | 13 | function formatDate(date) { 14 | let y = date.getFullYear(); 15 | let m = (date.getMonth() + 1).toString().padStart(2, '0'); 16 | let d = date.getDate().toString().padStart(2, '0'); 17 | let h = date.getHours().toString().padStart(2, '0'); 18 | let f = date.getMinutes().toString().padStart(2, '0'); 19 | let s = date.getSeconds().toString().padStart(2, '0'); 20 | let ss = date.getMilliseconds().toString().padStart(3, '0'); 21 | return y + '-' + m + '-' + d + ' ' + h + ':' + f + ':' + s + '.' + ss; 22 | } 23 | 24 | const timestamp = () => `${formatDate(new Date())}`; 25 | 26 | const log = (...args) => { 27 | if (logLevel == 'debug') { 28 | console.log(timestamp(), '[debug]', ...args); 29 | } 30 | }; 31 | 32 | const info = (...args) => { 33 | if (logLevel == 'debug' || logLevel == 'info') { 34 | console.info(timestamp(), '[info]', ...args); 35 | } 36 | }; 37 | 38 | const warn = (...args) => { 39 | if (logLevel == 'debug' || logLevel == 'info' || logLevel == 'warn') { 40 | console.warn(timestamp(), '[warn]', ...args); 41 | } 42 | }; 43 | 44 | const error = (...args) => { 45 | if (logLevel == 'debug' || logLevel == 'info' || logLevel == 'warn' || logLevel == 'error') { 46 | console.error(timestamp(), '[error]', ...args); 47 | } 48 | }; 49 | 50 | const dir = (...args) => { 51 | if (logLevel == 'debug') { 52 | console.dir(timestamp(), '[dir]', ...args); 53 | } 54 | }; 55 | 56 | const req = isProduction ? empty : requestLog; 57 | const setLogLevel = (level) => { 58 | logLevel = level; 59 | }; 60 | export default { 61 | error, 62 | log, 63 | info, 64 | warn, 65 | dir, 66 | req, 67 | setLogLevel 68 | }; 69 | -------------------------------------------------------------------------------- /src/sdk/utils/store.js: -------------------------------------------------------------------------------- 1 | import groupStore from './store/groupStore'; 2 | import infoStore from './store/infoStore'; 3 | import messageStore from './store/messageStore'; 4 | import noticeStore from './store/noticeStore'; 5 | import recentStore from './store/recentStore'; 6 | import rosterStore from './store/rosterStore'; 7 | 8 | export { groupStore, infoStore, messageStore, noticeStore, recentStore, rosterStore }; 9 | -------------------------------------------------------------------------------- /src/sdk/utils/store/groupStore.js: -------------------------------------------------------------------------------- 1 | import { getItem, removeItem, saveItem } from './storeBase'; 2 | 3 | const groupStore = { 4 | saveJoinedGroups: (groups) => { 5 | if (!groups) return; 6 | if (!Array.isArray(groups)) { 7 | groups = [groups]; 8 | } 9 | const gids = groups.map((item) => item.group_id || item); 10 | const savedIds = getItem('key_group_lists') || []; 11 | const ret = Array.from(new Set(gids.concat(savedIds))); 12 | saveItem('key_group_lists', ret); 13 | }, 14 | 15 | removeGroup: (gid) => { 16 | const allList = getItem('key_group_lists') || []; 17 | const index = allList.findIndex((item) => item === gid); 18 | if (index >= 0) { 19 | allList.splice(index, 1); 20 | saveItem('key_group_lists', allList); 21 | } 22 | }, 23 | 24 | getJoinedGroups: () => getItem('key_group_lists'), 25 | saveGroupInfo: (groups) => { 26 | if (!Array.isArray(groups)) { 27 | groups = [groups]; 28 | } 29 | const allGroupInfos = getItem('key_group_infos') || {}; 30 | groups.forEach((group) => { 31 | const sobj = {}; 32 | const { group_id } = group; 33 | Object.keys(group).forEach((key) => { 34 | const v = group[key]; 35 | typeof v !== 'undefined' && (sobj[key] = group[key]); 36 | }); 37 | allGroupInfos[group_id] = allGroupInfos[group_id] || {}; 38 | Object.assign(allGroupInfos[group_id], sobj); 39 | }); 40 | saveItem('key_group_infos', allGroupInfos); 41 | }, 42 | 43 | getGroupInfo: (group_id) => { 44 | const groupInfos = getItem('key_group_infos'); 45 | const ret = groupInfos[group_id] || {}; 46 | return Object.assign(ret, { 47 | group_id 48 | }); 49 | }, 50 | 51 | getGroupInfoList: () => { 52 | const groupIds = groupStore.getJoinedGroups() || []; 53 | const allInfos = groupStore.getAllGroupInfos() || {}; 54 | const ret = []; 55 | groupIds.forEach((gid) => { 56 | const ginfo = allInfos[gid] || {}; 57 | ret.push( 58 | Object.assign({}, ginfo, { 59 | group_id: gid 60 | }) 61 | ); 62 | }); 63 | return ret; 64 | }, 65 | 66 | getAllGroupInfos: () => getItem('key_group_infos') || {}, 67 | 68 | saveGroupMembers: (gid, members, replace) => { 69 | //replace 表示强制的代替, 强制存新的,用于不是增加的时候。 70 | if (!Array.isArray(members)) members = [members]; 71 | const allMap = getItem('key_group_members') || {}; 72 | const savedMembers = allMap[gid] || []; 73 | if (replace) { 74 | // 这个是第一次取,全量替换旧的 75 | allMap[gid] = members; 76 | saveItem('key_group_members', allMap); 77 | return; 78 | } 79 | members.forEach((uid) => { 80 | // 这个是来通知后的追加 81 | const index = savedMembers.findIndex((x) => x === uid); 82 | index < 0 && savedMembers.push(uid); 83 | }); 84 | allMap[gid] = savedMembers; 85 | }, 86 | 87 | removeGroupMembers: (gid, members) => { 88 | const allMap = getItem('key_group_members') || {}; 89 | const savedMembers = allMap[gid] || []; 90 | members.forEach((uid) => { 91 | const index = savedMembers.findIndex((x) => x === uid); 92 | index >= 0 && savedMembers.splice(index, 1); 93 | }); 94 | allMap[gid] = savedMembers; 95 | saveItem('key_group_members', allMap); 96 | }, 97 | 98 | getGroupMembers: (gid) => { 99 | const allMap = getItem('key_group_members') || {}; 100 | return allMap[gid] || []; 101 | }, 102 | 103 | clear: () => { 104 | removeItem('key_group_infos'); 105 | removeItem('key_group_members'); 106 | removeItem('key_group_lists'); 107 | } 108 | }; 109 | 110 | export default groupStore; 111 | -------------------------------------------------------------------------------- /src/sdk/utils/store/infoStore.js: -------------------------------------------------------------------------------- 1 | import log from '../log'; 2 | import { getItem, removeItem, saveItem } from './storeBase'; 3 | 4 | const saveUid = (uid) => { 5 | if (uid) { 6 | saveItem('key_user_id', uid + '', false, -1, true); 7 | } else { 8 | log.error('uid error:', uid); 9 | throw new Error('uid is error ...'); 10 | } 11 | }; 12 | 13 | const getUid = () => { 14 | let uid = getItem('key_user_id', false, -1, true); 15 | if (uid) return uid - 0; 16 | }; 17 | 18 | const saveAppid = (appid) => { 19 | if (appid) { 20 | saveItem('key_app_id', appid + '', false, -1, true); 21 | } else { 22 | log.error('Invalid appid :', appid); 23 | throw new Error('Invalid appid ...'); 24 | } 25 | }; 26 | 27 | const getAppid = () => { 28 | return getItem('key_app_id', false, -1, true); 29 | }; 30 | 31 | const removeUid = () => { 32 | removeItem('key_user_id'); 33 | }; 34 | 35 | const deleteToken = () => { 36 | removeItem('key_user_token'); 37 | }; 38 | 39 | const deleteLinkServer = () => { 40 | removeItem('key_user_link_server'); 41 | }; 42 | 43 | const deleteAesKey = () => { 44 | removeItem('key_user_aes_key'); 45 | }; 46 | 47 | const deleteDeviceSN = () => { 48 | removeItem('key_user_device_sn'); 49 | }; 50 | 51 | const deleteDeviceGuid = () => { 52 | removeItem('key_user_device_guid'); 53 | }; 54 | 55 | const deleteTokenAppId = () => { 56 | removeItem('key_user_token_app_id'); 57 | }; 58 | 59 | const infoStore = { 60 | saveToken: (token) => saveItem('key_user_token', token), 61 | getToken: () => getItem('key_user_token'), 62 | deleteToken, 63 | saveTokenAppId: (appId) => saveItem('key_user_token_app_id', appId), 64 | getTokenAppId: () => getItem('key_user_token_app_id'), 65 | deleteTokenAppId, 66 | saveLinkServer: (linkServer) => saveItem('key_user_link_server', linkServer), 67 | getLinkServer: () => getItem('key_user_link_server'), 68 | deleteLinkServer, 69 | getAesKey: () => getItem('key_user_aes_key'), 70 | saveAesKey: (key) => saveItem('key_user_aes_key', key), 71 | deleteAesKey, 72 | saveDeviceSN: (deviceSN) => saveItem('key_user_device_sn', deviceSN), 73 | getDeviceSN: () => { 74 | let ret = getItem('key_user_device_sn'); 75 | if (!ret) { 76 | ret = 999999999 + Math.floor(Math.random() * 2140000) + ''; 77 | } 78 | return ret - 0; 79 | }, 80 | deleteDeviceSN, 81 | saveUid, 82 | getUid, 83 | removeUid, 84 | getDeviceGuid: () => { 85 | const uid = getUid(); 86 | if (!uid) return ''; 87 | let ret = getItem('key_user_device_guid'); 88 | if (!ret) { 89 | ret = getUid() + '_' + Math.floor(Math.random() * 2147483648) + ''; 90 | saveItem('key_user_device_guid', ret); 91 | } 92 | return ret; 93 | }, 94 | deleteDeviceGuid, 95 | saveProfile: (profile) => saveItem('key_user_profile', profile), 96 | getProfile: () => getItem('key_user_profile'), 97 | 98 | clear: () => { 99 | deleteDeviceSN(); 100 | deleteAesKey(); 101 | deleteToken(); 102 | removeUid(); 103 | deleteDeviceGuid(); 104 | deleteTokenAppId(); 105 | deleteLinkServer(); 106 | }, 107 | 108 | saveAppid, 109 | getAppid 110 | }; 111 | 112 | export default infoStore; 113 | -------------------------------------------------------------------------------- /src/sdk/utils/store/noticeStore.js: -------------------------------------------------------------------------------- 1 | import { getItem } from './storeBase'; 2 | 3 | const noticeStore = { 4 | saveNotice: (/*meta*/) => { 5 | /*const items = getItem('key_notice_store') || []; 6 | items.push(meta); 7 | const len = items.length; 8 | if (len > 50) { 9 | items.splice(0, len - 50); 10 | } 11 | saveItem('key_notice_store', items);*/ 12 | }, 13 | getNotice: () => getItem('key_notice_store') || [], 14 | removeNotice: (/*meta*/) => { 15 | // 16 | } 17 | }; 18 | 19 | export default noticeStore; 20 | -------------------------------------------------------------------------------- /src/sdk/utils/store/rosterStore.js: -------------------------------------------------------------------------------- 1 | import { getItem, removeItem, saveItem } from './storeBase'; 2 | 3 | const rosterStore = { 4 | saveRosterList: (rosters = []) => { 5 | if (!Array.isArray(rosters)) { 6 | rosters = [rosters]; 7 | } 8 | const ids = rosters.map((item) => item.roster_user_id || item.user_id || item); 9 | const old_roster = rosterStore.getRosterList() || []; 10 | let new_roster; 11 | if (Array.isArray(old_roster)) { 12 | new_roster = Array.from(new Set(old_roster.concat(ids))); 13 | } else { 14 | // old roster is in bad format, abandon it. 15 | new_roster = ids; 16 | } 17 | saveItem('key_roster_lists', new_roster); 18 | 19 | rosterStore.saveRosterInfo(rosters); 20 | }, 21 | 22 | getRosterList: () => getItem('key_roster_lists'), 23 | 24 | getRosterInfoList: () => { 25 | const userids = rosterStore.getRosterList() || []; 26 | const allInfos = rosterStore.getAllRosterInfos() || {}; 27 | const ret = []; 28 | userids.forEach((uid) => { 29 | const uinfo = allInfos[uid] || {}; 30 | ret.push( 31 | Object.assign({}, uinfo, { 32 | user_id: uid 33 | }) 34 | ); 35 | }); 36 | return ret; 37 | }, 38 | removeRoster: (uid) => { 39 | const userids = rosterStore.getRosterList(); 40 | const index = userids.indexOf(uid); 41 | index >= 0 && userids.splice(index, 1); 42 | saveItem('key_roster_lists', userids); 43 | }, 44 | removeRosterList: () => removeItem('key_roster_lists'), 45 | saveRosterInfo: (rosters = []) => { 46 | if (!Array.isArray(rosters)) { 47 | rosters = [rosters]; 48 | } 49 | const allSavedMap = rosterStore.getAllRosterInfos() || {}; 50 | rosters.forEach((roster) => { 51 | const { user_id } = roster; 52 | const sobj = {}; 53 | Object.keys(roster).forEach((key) => { 54 | const v = roster[key]; 55 | typeof v !== 'undefined' && (sobj[key] = v); 56 | }); 57 | if (user_id) { 58 | allSavedMap[user_id] = allSavedMap[user_id] || {}; 59 | Object.assign(allSavedMap[user_id], sobj); 60 | // if (typeof relation !== 'undefined') { // 从 rosterlist 接口取的 61 | // Object.assign(allSavedMap[uid], { relation, alias }) 62 | // } else if (typeof username !== 'undefined') { // 从rost list 详情 取的 63 | // } 64 | } 65 | }); 66 | saveItem('key_roster_infos', allSavedMap); 67 | }, 68 | 69 | getRosterInfo: (uid) => { 70 | const userMap = getItem('key_roster_infos') || {}; 71 | let user = userMap[uid] || { user_id: uid }; 72 | return ( 73 | user && 74 | Object.assign(user, { 75 | user_id: uid 76 | }) 77 | ); 78 | }, 79 | getAllRosterInfos: () => { 80 | const ret = getItem('key_roster_infos'); 81 | return ret; 82 | }, 83 | 84 | clear: () => { 85 | removeItem('key_roster_infos'); 86 | removeItem('key_roster_lists'); 87 | } 88 | }; 89 | 90 | export default rosterStore; 91 | -------------------------------------------------------------------------------- /src/sdk/utils/store/storeBase.js: -------------------------------------------------------------------------------- 1 | import log from '../log'; 2 | import { transferToLong } from '../tools'; 3 | 4 | const PARTITION_NUMBER = 31; 5 | 6 | const getUid = () => { 7 | let uid = window.sessionStorage.getItem('key_user_id'); 8 | if (uid) { 9 | return uid - 0; 10 | } else { 11 | uid = window.localStorage.getItem('key_user_id'); 12 | if (uid) return uid - 0; 13 | } 14 | return; 15 | }; 16 | 17 | const partitionId = (key) => { 18 | return key % PARTITION_NUMBER; 19 | }; 20 | 21 | const saveItem = (key, item, hasuid = true, partition_key = -1, sessionStore = false) => { 22 | if (typeof item === 'undefined' || typeof key === 'undefined') { 23 | log.error('localstorage save error:', key, item); 24 | return; 25 | } 26 | 27 | var skey = key; 28 | if (hasuid) { 29 | const uid = getUid(); 30 | skey = uid + '_' + key; 31 | } 32 | if (partition_key >= 0) { 33 | skey = skey + '_' + partitionId(partition_key); 34 | } 35 | 36 | if (typeof item === 'string') { 37 | if (sessionStore) { 38 | window.sessionStorage.setItem(skey, item); 39 | } 40 | window.localStorage.setItem(skey, item); 41 | return; 42 | } 43 | 44 | let ret; 45 | if (Array.isArray(item)) { 46 | ret = { 47 | data: item 48 | }; 49 | } else { 50 | ret = item; 51 | } 52 | try { 53 | const itemString = JSON.stringify(ret); 54 | if (itemString) { 55 | if (sessionStore) { 56 | window.sessionStorage.setItem(skey, itemString); 57 | } 58 | window.localStorage.setItem(skey, itemString); 59 | } 60 | } catch (ex) { 61 | log.error('stringify error:', ex, skey, item); 62 | } 63 | }; 64 | 65 | const getItem = (key, hasuid = true, partition_key = -1, sessionStore = false) => { 66 | if (typeof key === 'undefined') { 67 | log.error('localstorage get error:', key); 68 | return; 69 | } 70 | 71 | var skey = key; 72 | if (hasuid) { 73 | const uid = getUid(); 74 | skey = uid + '_' + key; 75 | } 76 | if (partition_key >= 0) { 77 | skey = skey + '_' + partitionId(partition_key); 78 | } 79 | 80 | let itemString = null; 81 | if (sessionStore) { 82 | itemString = window.sessionStorage.getItem(skey); 83 | if (!itemString) { 84 | itemString = window.localStorage.getItem(skey); 85 | } 86 | } else { 87 | itemString = window.localStorage.getItem(skey); 88 | } 89 | if (!itemString) return undefined; 90 | let ret = itemString; 91 | try { 92 | ret = JSON.parse(itemString); 93 | } catch (ex) { 94 | // 95 | } 96 | ret = transferToLong(ret); 97 | return ret.data || ret; 98 | }; 99 | 100 | const removeItem = (key, hasuid = true, partition_key = -1) => { 101 | let skey = key; 102 | if (hasuid) { 103 | const uid = getUid(); 104 | skey = uid + '_' + key; 105 | } 106 | if (partition_key >= 0) { 107 | skey = skey + '_' + partitionId(partition_key); 108 | } 109 | window.localStorage.removeItem(skey); 110 | }; 111 | 112 | const removeAllItems = (key, hasuid = true) => { 113 | var i; 114 | for (i = 0; i < PARTITION_NUMBER; i++) { 115 | removeItem(key, hasuid, i); 116 | } 117 | removeItem(key, hasuid); 118 | }; 119 | 120 | export { saveItem, getItem, removeItem, removeAllItems }; 121 | -------------------------------------------------------------------------------- /src/ui/chatting/contact/conConversation.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /src/ui/chatting/contact/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/ui/chatting/content/group/header.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/ui/chatting/content/group/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/ui/chatting/content/group/memberList.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /src/ui/chatting/content/index.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /src/ui/chatting/content/notice/groupApplyNotice.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /src/ui/chatting/content/notice/groupNotice.vue: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/src/ui/chatting/content/notice/groupNotice.vue -------------------------------------------------------------------------------- /src/ui/chatting/content/notice/index.vue: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/src/ui/chatting/content/notice/index.vue -------------------------------------------------------------------------------- /src/ui/chatting/content/notice/rosterNotice.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/ui/chatting/content/notice/systemNotice.vue: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-top/floo-web/ac066b227567f6c7bb1584b09b7862f65aa8c823/src/ui/chatting/content/notice/systemNotice.vue -------------------------------------------------------------------------------- /src/ui/chatting/content/roster/header.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /src/ui/chatting/content/roster/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/ui/chatting/content/roster/info.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 106 | 107 | 112 | -------------------------------------------------------------------------------- /src/ui/chatting/content/verification/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/ui/layers/addfriend.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /src/ui/layers/addpop.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /src/ui/layers/callinvite.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /src/ui/layers/changeappid.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/ui/layers/image2.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 42 | 43 | 53 | -------------------------------------------------------------------------------- /src/ui/layers/index.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/ui/layers/joingroup.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /src/ui/layers/search.vue: -------------------------------------------------------------------------------- 1 | 21 | 22 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /src/ui/layers/video.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 42 | 43 | 53 | -------------------------------------------------------------------------------- /src/ui/login/bindacc.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /src/ui/login/bindreg.vue: -------------------------------------------------------------------------------- 1 | 23 | 24 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /src/ui/login/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/ui/login/regedit.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /src/ui/login/verifyinfo.vue: -------------------------------------------------------------------------------- 1 | 24 | 25 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /src/ui/store/header.js: -------------------------------------------------------------------------------- 1 | //collection.js 2 | 3 | const state = { 4 | headerStatus: 'contact', 5 | userProfile: {}, 6 | supportSafariAudio: false 7 | }; 8 | 9 | const headerRequestFlag = { 10 | profile: false 11 | }; 12 | 13 | const getters = { 14 | getHeaderStatus(state) { 15 | return state.headerStatus; 16 | }, 17 | getUserProfile(state) { 18 | return state.userProfile; 19 | }, 20 | getSupportSafariAudio(state) { 21 | return state.supportSafariAudio; 22 | } 23 | }; 24 | 25 | const mutations = { 26 | changeHeaderStatus(state, status) { 27 | state.headerStatus = status; 28 | }, 29 | 30 | changeHeaderUserProfile(state, profile) { 31 | state.userProfile = profile; 32 | }, 33 | 34 | changeSupportSafariAudio(state, status) { 35 | state.supportSafariAudio = status; 36 | } 37 | }; 38 | 39 | const actions = { 40 | actionChangeHeaderStatus(context, status) { 41 | context.commit('changeHeaderStatus', status); 42 | }, 43 | actionChangeHeaderUserProfile(context, profile) { 44 | context.commit('changeHeaderUserProfile', profile); 45 | }, 46 | actionGetHeaderProfile(context) { 47 | const { rootState } = context; 48 | rootState.im.userManage.asyncGetProfile(true).then((profile) => { 49 | context.commit('changeHeaderUserProfile', profile); 50 | }); 51 | }, 52 | actionLazyGetHeaderProfile(context) { 53 | const { state, rootState } = context; 54 | if (!state.userProfile.user_id && !headerRequestFlag.profile) { 55 | headerRequestFlag.profile = true; 56 | rootState.im.userManage.asyncGetProfile().then((profile) => { 57 | context.commit('changeHeaderUserProfile', profile); 58 | headerRequestFlag.profile = false; 59 | }); 60 | } 61 | }, 62 | actionChangeSupportSafariAudio(context, status) { 63 | context.commit('changeSupportSafariAudio', status); 64 | } 65 | }; 66 | export default { 67 | namespaced: true, //用于在全局引用此文件里的方法时标识这一个的文件名 68 | state, 69 | getters, 70 | mutations, 71 | actions 72 | }; 73 | -------------------------------------------------------------------------------- /src/ui/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | 4 | import Login from './login'; 5 | import header from './header'; 6 | import contact from './contact'; 7 | import chat from './content'; 8 | import content from './content'; 9 | import forward from './forward'; 10 | import setting from './setting'; 11 | import layer from './layer'; 12 | 13 | Vue.use(Vuex); 14 | 15 | export default new Vuex.Store({ 16 | modules: { 17 | login: Login, 18 | header, 19 | contact, 20 | chat, 21 | content, 22 | forward, 23 | setting, 24 | layer 25 | }, 26 | 27 | state: { 28 | appID: '', 29 | im: {} 30 | }, 31 | 32 | getters: { 33 | getAppID(state) { 34 | return state.appID; 35 | }, 36 | 37 | wim(state) { 38 | return state.im; 39 | }, 40 | im(state) { 41 | return state.im; 42 | } 43 | }, 44 | 45 | mutations: { 46 | changeAppID(state, newAppID) { 47 | state.appID = newAppID; 48 | }, 49 | 50 | saveIm(state, pim) { 51 | state.im = pim; 52 | } 53 | }, 54 | 55 | actions: { 56 | actionChangeAppID(context, appID) { 57 | context.commit('changeAppID', appID); 58 | }, 59 | 60 | actionSaveIm(context, pim) { 61 | context.commit('saveIm', pim); 62 | } 63 | } 64 | }); 65 | -------------------------------------------------------------------------------- /src/ui/store/layer.js: -------------------------------------------------------------------------------- 1 | //collection.js 2 | // import { toNumber } from '../third/tools'; 3 | 4 | const state = { 5 | // groupsetting, addfriend, creategroup, joingroup, search, addpop // qrlogin, qrprofile, qrgroup 6 | showing: '', 7 | showmask: false, 8 | imageUrl: '', 9 | videoUrl: '', 10 | appID: '' 11 | }; 12 | 13 | const getters = { 14 | getShowing(state) { 15 | return state.showing; 16 | }, 17 | getShowmask(state) { 18 | return state.showmask; 19 | }, 20 | 21 | gettingqrcode(state) { 22 | return state.qrcode; 23 | }, 24 | getImageUrl(state) { 25 | return state.imageUrl; 26 | }, 27 | getVideoUrl(state) { 28 | return state.videoUrl; 29 | }, 30 | getAppID(state) { 31 | return state.appID; 32 | } 33 | }; 34 | 35 | const mutations = { 36 | setShowing(state, x) { 37 | state.showing = x; 38 | }, 39 | setShowmask(state, x) { 40 | state.showmask = x; 41 | }, 42 | 43 | setImageUrl(state, x) { 44 | state.imageUrl = x; 45 | }, 46 | setVideoUrl(state, x) { 47 | state.videoUrl = x; 48 | }, 49 | 50 | setAppID(state, x) { 51 | state.appID = x; 52 | } 53 | }; 54 | 55 | const actions = { 56 | actionSetShowing(context, x) { 57 | if (x === 'addpop' && state.showing === 'addpop') { 58 | x = ''; 59 | } 60 | context.commit('setShowing', x); 61 | }, 62 | actionSetShowmask(context, x) { 63 | context.commit('setShowmask', x); 64 | }, 65 | 66 | actionSetQrcode(context, x) { 67 | context.commit('setQrcode', x); 68 | }, 69 | 70 | actionSetAppID(context, x) { 71 | context.commit('setAppID', x); 72 | }, 73 | 74 | actionSetImageUrl(context, x) { 75 | context.commit('setImageUrl', x); 76 | }, 77 | 78 | actionSetVideoUrl(context, x) { 79 | context.commit('setVideoUrl', x); 80 | } 81 | }; 82 | 83 | export default { 84 | namespaced: true, 85 | state, 86 | getters, 87 | mutations, 88 | actions 89 | }; 90 | -------------------------------------------------------------------------------- /src/ui/store/login.js: -------------------------------------------------------------------------------- 1 | //collection.js 2 | 3 | const state = { 4 | // appStatus: 'login', //初始化一个colects数组 5 | appStatus: 'login', //初始化一个colects数组 6 | login: false, 7 | loginLog: ['这里是登录日志..'], 8 | mobileSign: '', 9 | signMobile: '', 10 | loginInfo: {}, 11 | loginInfoList: [] 12 | }; 13 | 14 | const getters = { 15 | getAppStatus(state) { 16 | return state.appStatus; 17 | }, 18 | getLoginInStatus(state) { 19 | return state.login; 20 | }, 21 | getLoginLog(state) { 22 | return state.loginLog; 23 | }, 24 | getMobileSign(state) { 25 | return state.mobileSign; 26 | }, 27 | getSignMobile(state) { 28 | return state.signMobile; 29 | }, 30 | getLoginInfo(state) { 31 | return state.loginInfo; 32 | }, 33 | getLoginInfoList(state) { 34 | return state.loginInfoList; 35 | } 36 | }; 37 | 38 | const mutations = { 39 | changeAppStatus(state, status) { 40 | //如何变化collects,插入items 41 | state.appStatus = status; 42 | }, 43 | 44 | changeLoginStatus(state, status) { 45 | //如何变化collects,插入items 46 | state.login = status; 47 | }, 48 | 49 | addLoginLog(state, log) { 50 | let sr = [].concat(state.loginLog); 51 | sr.push(log); 52 | if (sr.length > 3) { 53 | sr = sr.slice(-3); // 只保留3条 54 | } 55 | state.loginLog = sr; 56 | }, 57 | setMobileSigh(state, sign) { 58 | state.mobileSign = sign; 59 | }, 60 | setSighMobile(state, mobile) { 61 | state.signMobile = mobile; 62 | }, 63 | setLoginInfo(state, info) { 64 | state.loginInfo = info; 65 | }, 66 | setLoginInfoList(state, list) { 67 | state.loginInfoList = list; 68 | } 69 | }; 70 | 71 | const actions = { 72 | actionChangeAppStatus(context, status) { 73 | //触发mutations里面的pushCollects ,传入数据形参item 对应到items 74 | context.commit('changeAppStatus', status); 75 | }, 76 | 77 | actionChangeLoginStatus(context, status) { 78 | context.commit('changeLoginStatus', status); 79 | }, 80 | 81 | actionAddLoginLog(context, log) { 82 | context.commit('addLoginLog', log); 83 | }, 84 | actionSetMobileSign(context, sign) { 85 | context.commit('setMobileSigh', sign); 86 | }, 87 | actionSetSignMobile(context, mobile) { 88 | context.commit('setSighMobile', mobile); 89 | }, 90 | actionSetLoginInfo(context, info) { 91 | context.commit('setLoginInfo', info); 92 | }, 93 | actionSetLoginInfoList(context, list) { 94 | context.commit('setLoginInfoList', list); 95 | } 96 | }; 97 | export default { 98 | namespaced: true, //用于在全局引用此文件里的方法时标识这一个的文件名 99 | state, 100 | getters, 101 | mutations, 102 | actions 103 | }; 104 | -------------------------------------------------------------------------------- /src/ui/store/setting.js: -------------------------------------------------------------------------------- 1 | //collection.js 2 | // import { toNumber } from '../third/tools'; 3 | 4 | const state = { 5 | settingInfo: {}, 6 | profileInfo: {}, 7 | callStatus: false 8 | }; 9 | 10 | const getters = { 11 | getSettingInfo(state) { 12 | return state.settingInfo; 13 | }, 14 | 15 | getProfileInfo(state) { 16 | return state.profileInfo; 17 | } 18 | }; 19 | 20 | const mutations = { 21 | setSettingInfo(state, x) { 22 | state.settingInfo = x; 23 | }, 24 | 25 | setProfileInfo(state, x) { 26 | state.profileInfo = x; 27 | } 28 | }; 29 | 30 | const actions = { 31 | actionGetProfile(context) { 32 | const { rootState } = context; 33 | rootState.im.userManage.asyncGetProfile(true).then((res) => { 34 | context.commit('setProfileInfo', res); 35 | }); 36 | }, 37 | actionGetSettingInfo(context) { 38 | const { rootState } = context; 39 | rootState.im.userManage.asyncGetSettings().then((res) => { 40 | context.commit('setSettingInfo', res); 41 | }); 42 | } 43 | }; 44 | 45 | export default { 46 | namespaced: true, 47 | state, 48 | getters, 49 | mutations, 50 | actions 51 | }; 52 | -------------------------------------------------------------------------------- /src/ui/support/content/group/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/ui/support/content/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 33 | -------------------------------------------------------------------------------- /src/ui/support/content/roster/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 46 | -------------------------------------------------------------------------------- /src/ui/support/content/roster/info.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 106 | 107 | 112 | -------------------------------------------------------------------------------- /src/ui/support/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 77 | -------------------------------------------------------------------------------- /src/ui/support/loading/index.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 51 | 52 | 62 | -------------------------------------------------------------------------------- /src/ui/support/minimize/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 93 | 94 | 114 | -------------------------------------------------------------------------------- /src/ui/support/skipping/index.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 71 | 72 | 113 | -------------------------------------------------------------------------------- /src/ui/third/base64.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | var Base64 = { 3 | // private property 4 | _keyStr: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=', 5 | 6 | // public method for encoding 7 | encode: function (input) { 8 | var output = ''; 9 | var chr1, chr2, chr3, enc1, enc2, enc3, enc4; 10 | var i = 0; 11 | 12 | input = Base64._utf8_encode(input); 13 | 14 | while (i < input.length) { 15 | chr1 = input.charCodeAt(i++); 16 | chr2 = input.charCodeAt(i++); 17 | chr3 = input.charCodeAt(i++); 18 | 19 | enc1 = chr1 >> 2; 20 | enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); 21 | enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); 22 | enc4 = chr3 & 63; 23 | 24 | if (isNaN(chr2)) { 25 | enc3 = enc4 = 64; 26 | } else if (isNaN(chr3)) { 27 | enc4 = 64; 28 | } 29 | 30 | output = output + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); 31 | } 32 | 33 | return output; 34 | }, 35 | 36 | // public method for decoding 37 | decode: function (input) { 38 | var output = ''; 39 | var chr1, chr2, chr3; 40 | var enc1, enc2, enc3, enc4; 41 | var i = 0; 42 | 43 | input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ''); 44 | 45 | while (i < input.length) { 46 | enc1 = this._keyStr.indexOf(input.charAt(i++)); 47 | enc2 = this._keyStr.indexOf(input.charAt(i++)); 48 | enc3 = this._keyStr.indexOf(input.charAt(i++)); 49 | enc4 = this._keyStr.indexOf(input.charAt(i++)); 50 | 51 | chr1 = (enc1 << 2) | (enc2 >> 4); 52 | chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); 53 | chr3 = ((enc3 & 3) << 6) | enc4; 54 | 55 | output = output + String.fromCharCode(chr1); 56 | 57 | if (enc3 != 64) { 58 | output = output + String.fromCharCode(chr2); 59 | } 60 | if (enc4 != 64) { 61 | output = output + String.fromCharCode(chr3); 62 | } 63 | } 64 | 65 | output = Base64._utf8_decode(output); 66 | 67 | return output; 68 | }, 69 | 70 | // private method for UTF-8 encoding 71 | _utf8_encode: function (string) { 72 | string = string.replace(/\r\n/g, '\n'); 73 | var utftext = ''; 74 | 75 | for (var n = 0; n < string.length; n++) { 76 | var c = string.charCodeAt(n); 77 | 78 | if (c < 128) { 79 | utftext += String.fromCharCode(c); 80 | } else if (c > 127 && c < 2048) { 81 | utftext += String.fromCharCode((c >> 6) | 192); 82 | utftext += String.fromCharCode((c & 63) | 128); 83 | } else { 84 | utftext += String.fromCharCode((c >> 12) | 224); 85 | utftext += String.fromCharCode(((c >> 6) & 63) | 128); 86 | utftext += String.fromCharCode((c & 63) | 128); 87 | } 88 | } 89 | 90 | return utftext; 91 | }, 92 | 93 | // private method for UTF-8 decoding 94 | _utf8_decode: function (utftext) { 95 | var string = ''; 96 | var i = 0; 97 | var c = 0; 98 | var c2 = 0; 99 | var c3 = 0; 100 | 101 | while (i < utftext.length) { 102 | c = utftext.charCodeAt(i); 103 | 104 | if (c < 128) { 105 | string += String.fromCharCode(c); 106 | i++; 107 | } else if (c > 191 && c < 224) { 108 | c2 = utftext.charCodeAt(i + 1); 109 | string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); 110 | i += 2; 111 | } else { 112 | c2 = utftext.charCodeAt(i + 1); 113 | c3 = utftext.charCodeAt(i + 2); 114 | string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); 115 | i += 3; 116 | } 117 | } 118 | return string; 119 | } 120 | }; 121 | 122 | export default Base64; 123 | -------------------------------------------------------------------------------- /src/ui/third/events.js: -------------------------------------------------------------------------------- 1 | const __ui_events = {}; 2 | window.__ui_events = __ui_events; 3 | 4 | const __judgeEvent = (name) => { 5 | if (typeof __ui_events[name] === 'undefined') { 6 | __ui_events[name] = []; 7 | } 8 | return __ui_events[name]; 9 | }; 10 | 11 | const bind = (name, func) => { 12 | const judge = __judgeEvent(name); 13 | const idx = judge.findIndex((f) => f.toString() === func.toString()); 14 | if (idx > -1) { 15 | __judgeEvent(name).splice(idx, 1); 16 | } 17 | __judgeEvent(name).push(func); 18 | }; 19 | 20 | const unBind = (name, func) => { 21 | const index = __judgeEvent(name).findIndex((item) => item.toString() === func.toString()); 22 | if (index >= 0) { 23 | __ui_events[name].splice(index, 1); 24 | } 25 | }; 26 | 27 | const unBindAll = (name) => { 28 | __ui_events[name] = null; 29 | }; 30 | 31 | const fire = (name, param) => { 32 | __judgeEvent(name).forEach((func) => { 33 | func(param); 34 | }); 35 | }; 36 | 37 | export { bind, unBind, unBindAll, fire }; 38 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | const webpack = require('webpack'); 3 | const path = require('path'); 4 | 5 | const devEnv = require('./config/webpack.dev.config'); 6 | const pordEnv = require('./config/webpack.prod.config'); 7 | 8 | let env = {}; 9 | if (process.env.NODE_ENV === 'development') { 10 | env = devEnv; 11 | } else if (process.env.NODE_ENV === 'production') { 12 | env = pordEnv; 13 | } 14 | 15 | module.exports = { 16 | lintOnSave: true, 17 | productionSourceMap: false, 18 | // outputDir: path.resolve(__dirname, 'dist'), // 默认dist 19 | chainWebpack: (config) => { 20 | config.resolve.alias 21 | .set('@src', path.join(__dirname, 'src')) 22 | // .set('vue', path.join('vue', 'dist', 'vue.js')) 23 | .set('/', path.join(__dirname, '/')) 24 | .set('@proj', path.resolve(__dirname)) 25 | .set('@build', path.join(__dirname, 'build')) 26 | .set('models', path.resolve(__dirname, 'src/models/')) 27 | .set('services', path.resolve(__dirname, 'src/services/')) 28 | .set('utils', path.resolve(__dirname, 'src/utils/')); 29 | }, 30 | 31 | configureWebpack: env 32 | }; 33 | --------------------------------------------------------------------------------