mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 18:44:16 +00:00
Merge branch 'Header-Proto' into 'master'
Header proto See merge request rstudio-development/comty-development!2
This commit is contained in:
commit
40afca9419
1
.gitignore
vendored
1
.gitignore
vendored
@ -22,3 +22,4 @@ package-lock.json
|
|||||||
src/locales/_build
|
src/locales/_build
|
||||||
src/locales/**/*.js
|
src/locales/**/*.js
|
||||||
android/
|
android/
|
||||||
|
direct
|
@ -1,5 +1,4 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
server_endpoint: 'https://comty.julioworld.club',
|
|
||||||
siteName: 'Comty',
|
siteName: 'Comty',
|
||||||
copyright: 'RageStudio©',
|
copyright: 'RageStudio©',
|
||||||
|
|
||||||
@ -9,7 +8,8 @@ module.exports = {
|
|||||||
DarkLogoPath: '/dark_logo.svg',
|
DarkLogoPath: '/dark_logo.svg',
|
||||||
|
|
||||||
resource_bundle: 'light_ng',
|
resource_bundle: 'light_ng',
|
||||||
sync_server: 'http://eu653-node.ragestudio.net:5500',
|
sync_server: 'http://localhost:5500',
|
||||||
|
server_endpoint: 'https://comty.pw',
|
||||||
|
|
||||||
g_recaptcha_key: '6Lc55uUUAAAAAEIACMVf3BUzAJSNCmI3RrjEirZ6',
|
g_recaptcha_key: '6Lc55uUUAAAAAEIACMVf3BUzAJSNCmI3RrjEirZ6',
|
||||||
g_recaptcha_secret: '6Lc55uUUAAAAAOP4OgUa5DpqJC-70t53AmW0lyYf',
|
g_recaptcha_secret: '6Lc55uUUAAAAAOP4OgUa5DpqJC-70t53AmW0lyYf',
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
Endpoints: {
|
|
||||||
comments_actions: "https://api.ragestudio.net/RSA-COMTY/r/comments?access_token=",
|
|
||||||
get_post_data: "https://api.ragestudio.net/RSA-COMTY/r/get-post-data?access_token=",
|
|
||||||
get_user_tags: "https://api.ragestudio.net/RSA-COMTY/r/user_tags?access_token=",
|
|
||||||
get_general_data: "https://api.ragestudio.net/RSA-COMTY/r/get-general-data?access_token=",
|
|
||||||
follow_user: "https://api.ragestudio.net/RSA-COMTY/r/follow-user?access_token=",
|
|
||||||
action_post: "https://api.ragestudio.net/RSA-COMTY/r/post-actions?access_token=",
|
|
||||||
get_posts: "https://api.ragestudio.net/RSA-COMTY/r/posts?access_token=",
|
|
||||||
find_user: "https://api.ragestudio.net/RSA-COMTY/r/find_user?access_token=",
|
|
||||||
search_endpoint: "https://api.ragestudio.net/RSA-COMTY/r/search?access_token=",
|
|
||||||
all_sessions: "https://api.ragestudio.net/RSA-COMTY/r/sessions?access_token=",
|
|
||||||
get_sessions: "https://api.ragestudio.net/RSA-COMTY/r/session_id?access_token=",
|
|
||||||
auth_endpoint: "https://api.ragestudio.net/RSA-COMTY/r/auth",
|
|
||||||
new_post: "https://comty.julioworld.club/api/new_post?access_token=",
|
|
||||||
get_config_endpoint: "https://api.ragestudio.net/RSA-COMTY/r/get-site-settings?access_token=",
|
|
||||||
get_userData_endpoint: "https://api.ragestudio.net/RSA-COMTY/r/get-user-data?access_token=",
|
|
||||||
update_userData_endpoint: "https://api.ragestudio.net/RSA-COMTY/r/update-user-data?access_token=",
|
|
||||||
removeToken: "https://api.ragestudio.net/RSA-COMTY/r/delete-access-token?access_token=",
|
|
||||||
register_endpoint: "https://api.ragestudio.net/RSA-COMTY/r/create-account",
|
|
||||||
resetPassword_endpoint: "https://api.ragestudio.net/RSA-COMTY/r/send-reset-password-email?access_token=",
|
|
||||||
}
|
|
||||||
}
|
|
@ -12,7 +12,7 @@ module.exports={
|
|||||||
all_sessions: "https://api.ragestudio.net/RSA-COMTY/r/sessions?access_token=",
|
all_sessions: "https://api.ragestudio.net/RSA-COMTY/r/sessions?access_token=",
|
||||||
get_sessions: "https://api.ragestudio.net/RSA-COMTY/r/session_id?access_token=",
|
get_sessions: "https://api.ragestudio.net/RSA-COMTY/r/session_id?access_token=",
|
||||||
auth_endpoint: "https://api.ragestudio.net/RSA-COMTY/r/auth",
|
auth_endpoint: "https://api.ragestudio.net/RSA-COMTY/r/auth",
|
||||||
new_post: "https://comty.julioworld.club/api/new_post?access_token=",
|
new_post: "https://comty.pw/api/new_post?access_token=",
|
||||||
get_config_endpoint: "https://api.ragestudio.net/RSA-COMTY/r/get-site-settings?access_token=",
|
get_config_endpoint: "https://api.ragestudio.net/RSA-COMTY/r/get-site-settings?access_token=",
|
||||||
get_userData: "https://api.ragestudio.net/RSA-COMTY/r/get-user-data?access_token=",
|
get_userData: "https://api.ragestudio.net/RSA-COMTY/r/get-user-data?access_token=",
|
||||||
update_userData_endpoint: "https://api.ragestudio.net/RSA-COMTY/r/update-user-data?access_token=",
|
update_userData_endpoint: "https://api.ragestudio.net/RSA-COMTY/r/update-user-data?access_token=",
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
"UUID": "C8mVSr-4nmPp2-pr5Vrz-CU4kg4",
|
"UUID": "C8mVSr-4nmPp2-pr5Vrz-CU4kg4",
|
||||||
"title": "Comty™",
|
"title": "Comty™",
|
||||||
"DevBuild": true,
|
"DevBuild": true,
|
||||||
"version": "0.3.18",
|
"version": "0.4.03",
|
||||||
"stage": "dev-pre",
|
"stage": "dev-pre",
|
||||||
"description": "",
|
"description": "",
|
||||||
"author": "RageStudio",
|
"author": "RageStudio",
|
||||||
@ -37,8 +37,10 @@
|
|||||||
"randomstring": "^1.1.5",
|
"randomstring": "^1.1.5",
|
||||||
"react-animations": "^1.0.0",
|
"react-animations": "^1.0.0",
|
||||||
"react-dazzle": "^1.4.0",
|
"react-dazzle": "^1.4.0",
|
||||||
|
"react-emoji": "^0.5.0",
|
||||||
"react-google-recaptcha": "^2.0.1",
|
"react-google-recaptcha": "^2.0.1",
|
||||||
"react-helmet": "^5.2.1",
|
"react-helmet": "^5.2.1",
|
||||||
|
"react-linkify": "^1.0.0-alpha",
|
||||||
"react-perfect-scrollbar": "^1.5.8",
|
"react-perfect-scrollbar": "^1.5.8",
|
||||||
"react-reveal": "^1.2.2",
|
"react-reveal": "^1.2.2",
|
||||||
"react-scripts": "^3.4.1",
|
"react-scripts": "^3.4.1",
|
||||||
|
@ -1,8 +1,27 @@
|
|||||||
import { API_Call, endpoints } from 'ycore'
|
import { API_Call, endpoints, get_early } from 'ycore'
|
||||||
import { comty_rsa } from '../rs_cloud/pre'
|
import { comty_rsa } from '../rs_cloud/pre'
|
||||||
|
|
||||||
export const comty_user = {
|
export const comty_user = {
|
||||||
setData: () => {},
|
setData: () => {},
|
||||||
|
data: {
|
||||||
|
avatar: (callback,key) => {
|
||||||
|
if(!key) return false
|
||||||
|
|
||||||
|
try {
|
||||||
|
const payload = {username: key}
|
||||||
|
get_early.user((err,res) => {
|
||||||
|
const d = JSON.parse(res)['data']
|
||||||
|
return callback(d.avatar)
|
||||||
|
},payload)
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
getFollowers: (callback, payload) => {
|
getFollowers: (callback, payload) => {
|
||||||
if (!payload)return false
|
if (!payload)return false
|
||||||
const { user_id } = payload
|
const { user_id } = payload
|
||||||
|
@ -43,13 +43,15 @@ export const sync = {
|
|||||||
|
|
||||||
},
|
},
|
||||||
FeedListen: (callback) => {
|
FeedListen: (callback) => {
|
||||||
const socket = io(endpoint);
|
const socket = io(`${endpoint}/feed`);
|
||||||
|
|
||||||
socket.on('pull_event', function (data) {
|
socket.on('pull_event', function (data) {
|
||||||
|
console.log(data)
|
||||||
callback(data)
|
callback(data)
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
emmitPost: (last_id) => {
|
emmitPost: (last_id) => {
|
||||||
const socket = io(endpoint);
|
const socket = io(`${endpoint}/feed`);
|
||||||
socket.emit('push_event', last_id);
|
socket.emit('push_event', last_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -14,7 +14,8 @@ import {
|
|||||||
__trendings,
|
__trendings,
|
||||||
__pro,
|
__pro,
|
||||||
__footer,
|
__footer,
|
||||||
__chats
|
__chats,
|
||||||
|
__searchBar
|
||||||
} from './renders.js'
|
} from './renders.js'
|
||||||
|
|
||||||
export const SwapMode = {
|
export const SwapMode = {
|
||||||
@ -215,7 +216,8 @@ export default class Secondary extends React.PureComponent {
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={styles.secondary_main}>
|
<div className={styles.secondary_main}>
|
||||||
{ycore.IsThisUser.pro() ? <__pro /> : <__pro />}
|
<__searchBar />
|
||||||
|
{/* {ycore.IsThisUser.pro() ? <__pro /> : <__pro />} */}
|
||||||
<__trendings data={trending_data} />
|
<__trendings data={trending_data} />
|
||||||
<__chats />
|
<__chats />
|
||||||
{__footer()}
|
{__footer()}
|
||||||
|
@ -105,7 +105,7 @@
|
|||||||
padding: 30px 30px 30px 35px;
|
padding: 30px 30px 30px 35px;
|
||||||
|
|
||||||
color: @secondary_container_1_color;
|
color: @secondary_container_1_color;
|
||||||
background-color: #2d2d2d;
|
background-color: #F8F6F8;
|
||||||
|
|
||||||
|
|
||||||
&.full_open {
|
&.full_open {
|
||||||
|
@ -336,3 +336,40 @@ export const __footer = () =>{
|
|||||||
v{ycore.AppInfo.version} | About | Legal | Help
|
v{ycore.AppInfo.version} | About | Legal | Help
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export class __searchBar extends React.Component{
|
||||||
|
state = {
|
||||||
|
value: '',
|
||||||
|
}
|
||||||
|
openSearcher = () => {
|
||||||
|
const { value } = this.state
|
||||||
|
if (value.length < 1) return false
|
||||||
|
if (value == /\s/) return false
|
||||||
|
ycore.SwapMode.openSearch(value);
|
||||||
|
}
|
||||||
|
onChange = e => {
|
||||||
|
const { value } = e.target
|
||||||
|
this.setState({ value: value })
|
||||||
|
if (ycore.AppSettings.auto_search_ontype == 'true') {
|
||||||
|
this.autosend()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handleKey = (e) =>{
|
||||||
|
if (e.key == 'Enter') {
|
||||||
|
this.openSearcher()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
render(){
|
||||||
|
return(
|
||||||
|
<div className={styles.search_bar}>
|
||||||
|
<input
|
||||||
|
placeholder="Search on Comty..."
|
||||||
|
onChange={this.onChange}
|
||||||
|
onPressEnter={this.openSearcher}
|
||||||
|
onKeyPress={this.handleKey}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -4,9 +4,9 @@
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
font-family: @__Global_general_font_family;
|
font-family: @__Global_general_font_family;
|
||||||
color: #ffffff;
|
color: #201F23;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
h1,h2,h3,h4,h5{color:#ffffff}
|
h1,h2,h3,h4,h5{color:#201F23}
|
||||||
}
|
}
|
||||||
|
|
||||||
.UserContainer {
|
.UserContainer {
|
||||||
@ -45,7 +45,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
font-family: 'Poppins', sans-serif;
|
font-family: 'Poppins', sans-serif;
|
||||||
margin: 0 0 0 50px;
|
margin: 0 0 0 50px;
|
||||||
color: #ffffff !important;
|
color: #201F23 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.textAgo {
|
.textAgo {
|
||||||
@ -81,7 +81,7 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
color: #ffffff !important;
|
color: #201F23 !important;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,7 +105,7 @@
|
|||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
font-family: "Poppins", sans-serif;
|
font-family: "Poppins", sans-serif;
|
||||||
color: #ffffff;
|
color: #201F23;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
letter-spacing: -0.3px;
|
letter-spacing: -0.3px;
|
||||||
@ -159,7 +159,7 @@
|
|||||||
overflow-y: scroll!important;
|
overflow-y: scroll!important;
|
||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: #ffffff;
|
background-color: #201F23;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
|
|
||||||
.comment_title {
|
.comment_title {
|
||||||
@ -202,7 +202,7 @@
|
|||||||
right: 0;
|
right: 0;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
background-color: #ffffffd7;
|
background-color: #201F23d7;
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
padding-bottom: 60px;
|
padding-bottom: 60px;
|
||||||
border-radius: 0 0 0 32px;
|
border-radius: 0 0 0 32px;
|
||||||
@ -229,7 +229,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.search_wrapper{
|
.search_wrapper{
|
||||||
color: #ffffff;
|
color: #201F23;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 82%;
|
width: 82%;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
@ -255,7 +255,7 @@
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
color: #ffffff;
|
color: #201F23;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
.search_title {
|
.search_title {
|
||||||
@ -272,7 +272,7 @@
|
|||||||
margin: 0 5px 0 8px;
|
margin: 0 5px 0 8px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
color: #ffffff;
|
color: #201F23;
|
||||||
line-height: 25px;
|
line-height: 25px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -285,12 +285,12 @@
|
|||||||
|
|
||||||
.secondary_hastags {
|
.secondary_hastags {
|
||||||
font-family: @__Global_general_font_family;
|
font-family: @__Global_general_font_family;
|
||||||
color: #ffffff;
|
color: #201F23;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
|
||||||
|
|
||||||
.secondary_hastags_title{
|
.secondary_hastags_title{
|
||||||
h2{color: #ffffff;}
|
h2{color: #201F23;}
|
||||||
}
|
}
|
||||||
|
|
||||||
.secondary_hastags_body{
|
.secondary_hastags_body{
|
||||||
@ -336,7 +336,7 @@
|
|||||||
height: 140px;
|
height: 140px;
|
||||||
|
|
||||||
h1{
|
h1{
|
||||||
color: #ffffff;
|
color: #201F23;
|
||||||
font-family: "Poppins", sans-serif;
|
font-family: "Poppins", sans-serif;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
@ -369,3 +369,30 @@
|
|||||||
letter-spacing: 2px;
|
letter-spacing: 2px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.search_bar {
|
||||||
|
height: 80px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
input {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
color: @body-color;
|
||||||
|
padding: 0 54px;
|
||||||
|
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 56.966 56.966' fill='%23c1c7cd'%3e%3cpath d='M55.146 51.887L41.588 37.786A22.926 22.926 0 0046.984 23c0-12.682-10.318-23-23-23s-23 10.318-23 23 10.318 23 23 23c4.761 0 9.298-1.436 13.177-4.162l13.661 14.208c.571.593 1.339.92 2.162.92.779 0 1.518-.297 2.079-.837a3.004 3.004 0 00.083-4.242zM23.984 6c9.374 0 17 7.626 17 17s-7.626 17-17 17-17-7.626-17-17 7.626-17 17-17z'/%3e%3c/svg%3e");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: 16px;
|
||||||
|
background-position: 25px 48%;
|
||||||
|
font-family: @body-font;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 15px;
|
||||||
|
&::placeholder {
|
||||||
|
color: @input-chat-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,8 +16,6 @@ export default class Sider_Default extends React.PureComponent {
|
|||||||
<div className={styles.left_sider_wrapper}>
|
<div className={styles.left_sider_wrapper}>
|
||||||
<antd.Layout.Sider
|
<antd.Layout.Sider
|
||||||
trigger={null}
|
trigger={null}
|
||||||
collapsed
|
|
||||||
collapsedWidth='80'
|
|
||||||
className={styles.left_sider_container}
|
className={styles.left_sider_container}
|
||||||
>
|
>
|
||||||
<div className={styles.left_sider_brandholder}>
|
<div className={styles.left_sider_brandholder}>
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.ant-menu-item {
|
.ant-menu-item {
|
||||||
color: @left_sider_color;
|
color: #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
.anticon {
|
.anticon {
|
||||||
@ -129,9 +129,9 @@
|
|||||||
:global {
|
:global {
|
||||||
.ant-menu-item {
|
.ant-menu-item {
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
margin: 2px auto 2px auto;
|
margin: 2px auto 2px 24px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: center;
|
text-align: left;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -3,7 +3,6 @@ import styles from './styles.less'
|
|||||||
import * as ycore from 'ycore'
|
import * as ycore from 'ycore'
|
||||||
import * as antd from 'antd'
|
import * as antd from 'antd'
|
||||||
import { CustomIcons, MainFeed, PostCreator } from 'components'
|
import { CustomIcons, MainFeed, PostCreator } from 'components'
|
||||||
import { SetHeaderSearchType } from 'components/HeaderSearch'
|
|
||||||
import * as Icons from '@ant-design/icons'
|
import * as Icons from '@ant-design/icons'
|
||||||
import Icon from '@ant-design/icons'
|
import Icon from '@ant-design/icons'
|
||||||
import Follow_btn from './components/Follow_btn.js'
|
import Follow_btn from './components/Follow_btn.js'
|
||||||
@ -57,7 +56,7 @@ class UserProfile extends React.PureComponent {
|
|||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.initUser(this.props.regx)
|
this.initUser(this.props.regx)
|
||||||
SetHeaderSearchType.disable()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
initUser = e => {
|
initUser = e => {
|
||||||
|
@ -106,7 +106,6 @@ class PrimaryLayout extends React.Component {
|
|||||||
id="primaryContent"
|
id="primaryContent"
|
||||||
className={styles.primary_layout_content}
|
className={styles.primary_layout_content}
|
||||||
>
|
>
|
||||||
<HeaderSearch />
|
|
||||||
{children}
|
{children}
|
||||||
</Content>
|
</Content>
|
||||||
</PageTransition>
|
</PageTransition>
|
||||||
|
11
src/pages/chats/Events.js
Normal file
11
src/pages/chats/Events.js
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
module.exports = {
|
||||||
|
COMMUNITY_CHAT:"COMMUNITY_CHAT",
|
||||||
|
USER_CONNECTED:"USER_CONNECTED",
|
||||||
|
MESSAGE_RECIEVED:"MESSAGE_RECIEVED",
|
||||||
|
MESSAGE_SENT:"MESSAGE_SENT",
|
||||||
|
USER_DISCONNECTED:"USER_DISCONNECTED",
|
||||||
|
TYPING:"TYPING",
|
||||||
|
VERIFY_USER:"VERIFY_USER",
|
||||||
|
LOGOUT:"LOGOUT",
|
||||||
|
PRIVATE_MESSAGE:"PRIVATE_MESSAGE"
|
||||||
|
}
|
197
src/pages/chats/chats/ChatContainer.js
Normal file
197
src/pages/chats/chats/ChatContainer.js
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import SideBar from './SideBar'
|
||||||
|
import { MESSAGE_SENT, MESSAGE_RECIEVED, TYPING, PRIVATE_MESSAGE } from '../Events'
|
||||||
|
|
||||||
|
import Messages from '../messages/Messages'
|
||||||
|
import MessageInput from '../messages/MessageInput'
|
||||||
|
|
||||||
|
import * as ycore from 'ycore'
|
||||||
|
import * as antd from 'antd'
|
||||||
|
import * as Icons from '@ant-design/icons'
|
||||||
|
import styles from '../styles.less'
|
||||||
|
import classnames from 'classnames'
|
||||||
|
|
||||||
|
const userData = ycore.userData();
|
||||||
|
|
||||||
|
export default class ChatContainer extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
chats:[],
|
||||||
|
activeChat:null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const { socket } = this.props
|
||||||
|
this.initSocket(socket)
|
||||||
|
}
|
||||||
|
|
||||||
|
initSocket(socket){
|
||||||
|
socket.on(PRIVATE_MESSAGE, this.addChat)
|
||||||
|
}
|
||||||
|
|
||||||
|
sendOpenPrivateMessage = (reciever) => {
|
||||||
|
const { socket, user } = this.props
|
||||||
|
const { activeChat } = this.state
|
||||||
|
socket.emit(PRIVATE_MESSAGE, {reciever, sender:user.name, activeChat})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reset the chat back to only the chat passed in.
|
||||||
|
* @param chat {Chat}
|
||||||
|
*/
|
||||||
|
resetChat = (chat)=>{
|
||||||
|
return this.addChat(chat, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Adds chat to the chat container, if reset is true removes all chats
|
||||||
|
* and sets that chat to the main chat.
|
||||||
|
* Sets the message and typing socket events for the chat.
|
||||||
|
*
|
||||||
|
* @param chat {Chat} the chat to be added.
|
||||||
|
* @param reset {boolean} if true will set the chat as the only chat.
|
||||||
|
*/
|
||||||
|
addChat = (chat, reset = false)=>{
|
||||||
|
const { socket } = this.props
|
||||||
|
const { chats } = this.state
|
||||||
|
|
||||||
|
console.log(chat)
|
||||||
|
|
||||||
|
const newChats = reset ? [chat] : [...chats, chat]
|
||||||
|
this.setState({chats:newChats, activeChat:reset ? chat : this.state.activeChat})
|
||||||
|
|
||||||
|
const messageEvent = `${MESSAGE_RECIEVED}-${chat.id}`
|
||||||
|
const typingEvent = `${TYPING}-${chat.id}`
|
||||||
|
|
||||||
|
socket.on(typingEvent, this.updateTypingInChat(chat.id))
|
||||||
|
socket.on(messageEvent, this.addMessageToChat(chat.id))
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns a function that will
|
||||||
|
* adds message to chat with the chatId passed in.
|
||||||
|
*
|
||||||
|
* @param chatId {number}
|
||||||
|
*/
|
||||||
|
addMessageToChat = (chatId)=>{
|
||||||
|
return message => {
|
||||||
|
const { chats } = this.state
|
||||||
|
let newChats = chats.map((chat)=>{
|
||||||
|
if(chat.id === chatId)
|
||||||
|
chat.messages.push(message)
|
||||||
|
return chat
|
||||||
|
})
|
||||||
|
|
||||||
|
this.setState({chats:newChats})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Updates the typing of chat with id passed in.
|
||||||
|
* @param chatId {number}
|
||||||
|
*/
|
||||||
|
updateTypingInChat = (chatId) =>{
|
||||||
|
return ({isTyping, user})=>{
|
||||||
|
if(user !== this.props.user.name){
|
||||||
|
|
||||||
|
const { chats } = this.state
|
||||||
|
|
||||||
|
let newChats = chats.map((chat)=>{
|
||||||
|
if(chat.id === chatId){
|
||||||
|
if(isTyping && !chat.typingUsers.includes(user)){
|
||||||
|
chat.typingUsers.push(user)
|
||||||
|
}else if(!isTyping && chat.typingUsers.includes(user)){
|
||||||
|
chat.typingUsers = chat.typingUsers.filter(u => u !== user)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return chat
|
||||||
|
})
|
||||||
|
this.setState({chats:newChats})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Adds a message to the specified chat
|
||||||
|
* @param chatId {number} The id of the chat to be added to.
|
||||||
|
* @param message {string} The message to be added to the chat.
|
||||||
|
*/
|
||||||
|
sendMessage = (chatId, message)=>{
|
||||||
|
const { socket } = this.props
|
||||||
|
socket.emit(MESSAGE_SENT, {chatId, message} )
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sends typing status to server.
|
||||||
|
* chatId {number} the id of the chat being typed in.
|
||||||
|
* typing {boolean} If the user is typing still or not.
|
||||||
|
*/
|
||||||
|
sendTyping = (chatId, isTyping)=>{
|
||||||
|
const { socket } = this.props
|
||||||
|
socket.emit(TYPING, {chatId, isTyping})
|
||||||
|
}
|
||||||
|
|
||||||
|
setActiveChat = (activeChat)=>{
|
||||||
|
this.setState({activeChat})
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
const { user } = this.props
|
||||||
|
const { chats, activeChat } = this.state
|
||||||
|
return (
|
||||||
|
<div className={styles.chats_wrapper}>
|
||||||
|
<SideBar
|
||||||
|
chats={chats}
|
||||||
|
user={user}
|
||||||
|
activeChat={activeChat}
|
||||||
|
setActiveChat={this.setActiveChat}
|
||||||
|
onSendPrivateMessage={this.sendOpenPrivateMessage}
|
||||||
|
/>
|
||||||
|
{
|
||||||
|
activeChat !== null ? (
|
||||||
|
<div className={styles.app}>
|
||||||
|
<div className={styles.wrapper}>
|
||||||
|
<div className={styles.hat_area}>
|
||||||
|
<div className={styles.chat_area_main}>
|
||||||
|
|
||||||
|
<Messages
|
||||||
|
messages={activeChat.messages}
|
||||||
|
user={user}
|
||||||
|
typingUsers={activeChat.typingUsers}
|
||||||
|
/>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<MessageInput
|
||||||
|
sendMessage={
|
||||||
|
(message)=>{
|
||||||
|
this.sendMessage(activeChat.id, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sendTyping={
|
||||||
|
(isTyping)=>{
|
||||||
|
this.sendTyping(activeChat.id, isTyping)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
):
|
||||||
|
<div className="chat-room choose">
|
||||||
|
<antd.Result
|
||||||
|
icon={<Icons.SmileOutlined />}
|
||||||
|
title="Hey, you still haven't started chatting with anyone"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
81
src/pages/chats/chats/SideBar.js
Normal file
81
src/pages/chats/chats/SideBar.js
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import * as ycore from 'ycore'
|
||||||
|
import * as antd from 'antd'
|
||||||
|
import styles from '../styles.less'
|
||||||
|
|
||||||
|
const userData = ycore.userData()
|
||||||
|
|
||||||
|
export default class SideBar extends React.PureComponent{
|
||||||
|
constructor(props){
|
||||||
|
super(props)
|
||||||
|
this.state = {
|
||||||
|
reciever:""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handleSubmit = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
const { reciever } = this.state
|
||||||
|
const { onSendPrivateMessage } = this.props
|
||||||
|
|
||||||
|
onSendPrivateMessage(reciever)
|
||||||
|
this.setState({reciever:""})
|
||||||
|
}
|
||||||
|
|
||||||
|
render(){
|
||||||
|
const { chats, activeChat, user, setActiveChat } = this.props
|
||||||
|
const { reciever } = this.state
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.chat_sider}>
|
||||||
|
|
||||||
|
<form onSubmit={this.handleSubmit} className="search">
|
||||||
|
<input
|
||||||
|
placeholder="Search"
|
||||||
|
type="text"
|
||||||
|
value={reciever}
|
||||||
|
onChange={(e)=>{ this.setState({reciever:e.target.value}) }}/>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div
|
||||||
|
className="users"
|
||||||
|
ref='users'
|
||||||
|
onClick={(e)=>{ (e.target === this.refs.user) && setActiveChat(null) }}>
|
||||||
|
|
||||||
|
{
|
||||||
|
chats.map((chat)=>{
|
||||||
|
if(chat.name){
|
||||||
|
const lastMessage = chat.messages[chat.messages.length - 1];
|
||||||
|
const chatSideName = chat.users.find((name)=>{
|
||||||
|
return name !== user.name
|
||||||
|
}) || "Unknown"
|
||||||
|
const ops_adata = chat.udata.find((i) =>{
|
||||||
|
return i.user !== user.name? i.avatar : null
|
||||||
|
})
|
||||||
|
const classNames = (activeChat && activeChat.id === chat.id) ? 'active' : ''
|
||||||
|
return(
|
||||||
|
<div
|
||||||
|
key={chat.id}
|
||||||
|
className={`user ${classNames}`}
|
||||||
|
onClick={ ()=>{ setActiveChat(chat) } }
|
||||||
|
>
|
||||||
|
<div className="user-photo"> <antd.Avatar size="small" src={ops_adata.avatar} /> </div>
|
||||||
|
<div className="user-info">
|
||||||
|
<div className="name">{chatSideName}</div>
|
||||||
|
{lastMessage && <div className="last-message">{lastMessage.message}</div>}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,72 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
import * as ycore from 'ycore'
|
||||||
|
import * as antd from 'antd'
|
||||||
|
import * as Icons from '@ant-design/icons'
|
||||||
|
import io from 'socket.io-client'
|
||||||
|
import config from 'config'
|
||||||
|
import ReactEmoji from 'react-emoji';
|
||||||
|
import { USER_CONNECTED, LOGOUT } from './Events'
|
||||||
|
import ChatContainer from './chats/ChatContainer'
|
||||||
|
|
||||||
export default class extends React.PureComponent{
|
|
||||||
render(){
|
const userData = ycore.userData()
|
||||||
return(
|
|
||||||
<div>Chats</div>
|
const prefix = '[Messaging Socket] '
|
||||||
)
|
const socketUrl = io(`${config.sync_server}/messaging_socket`);
|
||||||
|
|
||||||
|
export default class Chats extends React.Component{
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
socket:null,
|
||||||
|
user:null,
|
||||||
|
conn: false
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.initSocket()
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Connect to and initializes the socket.
|
||||||
|
*/
|
||||||
|
initSocket = async ()=>{
|
||||||
|
const socket = socketUrl
|
||||||
|
|
||||||
|
if(!this.state.conn){
|
||||||
|
await socket.on('connect', ()=>{
|
||||||
|
console.log(prefix, "Connected");
|
||||||
|
const payload = { id: userData.UserID, name: userData.username, avatar: userData.avatar }
|
||||||
|
socket.emit(USER_CONNECTED, payload);
|
||||||
|
this.setState({user: payload, conn: true})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({socket})
|
||||||
|
|
||||||
|
socket.on('disconnect', () => {
|
||||||
|
this.setState({ conn: false })
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.on('reconnecting', () =>{
|
||||||
|
console.log(prefix, 'Trying to reconnect')
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { socket, user } = this.state
|
||||||
|
return (
|
||||||
|
<div className="container">
|
||||||
|
{
|
||||||
|
!user ?
|
||||||
|
<h2>initializes....</h2>
|
||||||
|
:
|
||||||
|
<ChatContainer socket={socket} user={user} />
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
111
src/pages/chats/messages/MessageInput.js
Normal file
111
src/pages/chats/messages/MessageInput.js
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import styles from '../styles.less'
|
||||||
|
|
||||||
|
export default class MessageInput extends Component {
|
||||||
|
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.state = {
|
||||||
|
message:"",
|
||||||
|
isTyping:false
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
handleSubmit = (e)=>{
|
||||||
|
e.preventDefault()
|
||||||
|
this.sendMessage()
|
||||||
|
this.setState({message:""})
|
||||||
|
}
|
||||||
|
|
||||||
|
sendMessage = ()=>{
|
||||||
|
this.props.sendMessage(this.state.message)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.stopCheckingTyping()
|
||||||
|
}
|
||||||
|
|
||||||
|
sendTyping = ()=>{
|
||||||
|
this.lastUpdateTime = Date.now()
|
||||||
|
if(!this.state.isTyping){
|
||||||
|
this.setState({isTyping:true})
|
||||||
|
this.props.sendTyping(true)
|
||||||
|
this.startCheckingTyping()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* startCheckingTyping
|
||||||
|
* Start an interval that checks if the user is typing.
|
||||||
|
*/
|
||||||
|
startCheckingTyping = ()=>{
|
||||||
|
console.log("Typing");
|
||||||
|
this.typingInterval = setInterval(()=>{
|
||||||
|
if((Date.now() - this.lastUpdateTime) > 300){
|
||||||
|
this.setState({isTyping:false})
|
||||||
|
this.stopCheckingTyping()
|
||||||
|
}
|
||||||
|
}, 300)
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* stopCheckingTyping
|
||||||
|
* Start the interval from checking if the user is typing.
|
||||||
|
*/
|
||||||
|
stopCheckingTyping = ()=>{
|
||||||
|
console.log("Stop Typing");
|
||||||
|
if(this.typingInterval){
|
||||||
|
clearInterval(this.typingInterval)
|
||||||
|
this.props.sendTyping(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { message } = this.state
|
||||||
|
return (
|
||||||
|
<form
|
||||||
|
onSubmit={ this.handleSubmit }
|
||||||
|
className="message-form">
|
||||||
|
<div className={styles.chat_area_footer}>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-video">
|
||||||
|
<path d="M23 7l-7 5 7 5V7z" />
|
||||||
|
<rect x="1" y="5" width="15" height="14" rx="2" ry="2" /></svg>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-image">
|
||||||
|
<rect x="3" y="3" width="18" height="18" rx="2" ry="2" />
|
||||||
|
<circle cx="8.5" cy="8.5" r="1.5" />
|
||||||
|
<path d="M21 15l-5-5L5 21" /></svg>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-plus-circle">
|
||||||
|
<circle cx="12" cy="12" r="10" />
|
||||||
|
<path d="M12 8v8M8 12h8" /></svg>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-paperclip">
|
||||||
|
<path d="M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.48" /></svg>
|
||||||
|
<input
|
||||||
|
id = "message"
|
||||||
|
ref = {"messageinput"}
|
||||||
|
type = "text"
|
||||||
|
value = { message }
|
||||||
|
autoComplete = {'off'}
|
||||||
|
placeholder = "Type something here..."
|
||||||
|
onKeyUp = { e => { e.keyCode !== 13 && this.sendTyping() } }
|
||||||
|
onChange = {
|
||||||
|
({target})=>{
|
||||||
|
this.setState({message:target.value})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-smile">
|
||||||
|
<circle cx="12" cy="12" r="10" />
|
||||||
|
<path d="M8 14s1.5 2 4 2 4-2 4-2M9 9h.01M15 9h.01" /></svg>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather feather-thumbs-up">
|
||||||
|
<path d="M14 9V5a3 3 0 00-3-3l-4 9v11h11.28a2 2 0 002-1.7l1.38-9a2 2 0 00-2-2.3zM7 22H4a2 2 0 01-2-2v-7a2 2 0 012-2h3" /></svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
59
src/pages/chats/messages/Messages.js
Normal file
59
src/pages/chats/messages/Messages.js
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
import React, { Component } from 'react';
|
||||||
|
import styles from '../styles.less'
|
||||||
|
import classnames from 'classnames'
|
||||||
|
|
||||||
|
export default class Messages extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
|
||||||
|
this.scrollDown = this.scrollDown.bind(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
scrollDown(){
|
||||||
|
const { container } = this.refs
|
||||||
|
container.scrollTop = container.scrollHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.scrollDown()
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
this.scrollDown()
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { messages, user, typingUsers } = this.props
|
||||||
|
return (
|
||||||
|
<div ref='container' style={{ width: '100%' }}>
|
||||||
|
{
|
||||||
|
messages.map((mes)=>{
|
||||||
|
return (
|
||||||
|
<div className={classnames(styles.chat_msg, {[styles.owner]: mes.sender == user.name? true : false })} key={mes.id}>
|
||||||
|
<div className={styles.chat_msg_profile}>
|
||||||
|
<img className={styles.chat_msg_img} src={mes.avatar} />
|
||||||
|
<div className={styles.chat_msg_date}>{mes.time}</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.chat_msg_content}>
|
||||||
|
<div className={styles.chat_msg_text}>{mes.message}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
{
|
||||||
|
typingUsers.map((name)=>{
|
||||||
|
return (
|
||||||
|
<div key={name} className="typing-user">
|
||||||
|
{`${name} is typing . . .`}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
361
src/pages/chats/styles.less
Normal file
361
src/pages/chats/styles.less
Normal file
@ -0,0 +1,361 @@
|
|||||||
|
@import '~themes/index.less';
|
||||||
|
|
||||||
|
.hat_area{
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chats_wrapper{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
height: 80vh;
|
||||||
|
width: 100%;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 9px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat_sider{
|
||||||
|
border-right: 1px #eef2f4 solid;
|
||||||
|
padding: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dark-mode {
|
||||||
|
@body-bg-color: #1d1d1d;
|
||||||
|
@theme-bg-color: #27292d;
|
||||||
|
@border-color: #323336;
|
||||||
|
@body-color: #d1d1d2;
|
||||||
|
@active-conversation-bg: linear-gradient(
|
||||||
|
to right,
|
||||||
|
rgba(47, 50, 56, 0.54),
|
||||||
|
rgba(238, 242, 244, 0) 100%
|
||||||
|
);
|
||||||
|
@msg-hover-bg: rgba(47, 50, 56, 0.54);
|
||||||
|
@chat-text-bg: #383b40;
|
||||||
|
@chat-text-color: #b5b7ba;
|
||||||
|
@msg-date: #626466;
|
||||||
|
@msg-message: var(@msg-date);
|
||||||
|
@overlay-bg: linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
rgba(0, 0, 0, 0) 0%,
|
||||||
|
#27292d 65%,
|
||||||
|
#27292d 100%
|
||||||
|
);
|
||||||
|
@input-bg: #2f3236;
|
||||||
|
@chat-header-bg: linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
#27292d 0%,
|
||||||
|
#27292d 78%,
|
||||||
|
rgba(255, 255, 255, 0) 100%
|
||||||
|
);
|
||||||
|
@settings-icon-color: #7c7e80;
|
||||||
|
@developer-color: var(@border-color);
|
||||||
|
@button-bg-color: #393b40;
|
||||||
|
@button-color: var(@body-color);
|
||||||
|
@input-chat-color: #6f7073;
|
||||||
|
@detail-font-color: var(@input-chat-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
outline: none;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: @body-bg-color;
|
||||||
|
font-family: @body-font;
|
||||||
|
color: @body-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
html {
|
||||||
|
box-sizing: border-box;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background-color: @theme-bg-color;
|
||||||
|
max-width: 1600px;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
margin: 0 auto;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation_area{
|
||||||
|
width: 340px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat_area {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.conversation_area {
|
||||||
|
border-right: 1px solid @border-color;
|
||||||
|
overflow-y: auto;
|
||||||
|
overflow-x: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg_profile {
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
border-radius: 50%;
|
||||||
|
object-fit: cover;
|
||||||
|
margin-right: 15px;
|
||||||
|
&.group {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
background-color: @border-color;
|
||||||
|
|
||||||
|
svg {
|
||||||
|
width: 60%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: 0.2s;
|
||||||
|
position: relative;
|
||||||
|
&:hover {
|
||||||
|
background-color: @msg-hover-bg;
|
||||||
|
}
|
||||||
|
&.active {
|
||||||
|
background: @active-conversation-bg;
|
||||||
|
border-left: 4px solid @theme-color;
|
||||||
|
}
|
||||||
|
&.online:before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
background-color: #23be7e;
|
||||||
|
width: 9px;
|
||||||
|
height: 9px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: 2px solid @theme-bg-color;
|
||||||
|
left: 50px;
|
||||||
|
bottom: 19px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg_username {
|
||||||
|
margin-bottom: 4px;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg_detail {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg_content {
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 13px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg_message {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
color: @msg-message;
|
||||||
|
}
|
||||||
|
|
||||||
|
.msg_date {
|
||||||
|
font-size: 14px;
|
||||||
|
color: @msg-date;
|
||||||
|
margin-left: 3px;
|
||||||
|
&:before {
|
||||||
|
content: "•";
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat_area {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: auto;
|
||||||
|
&.profile {
|
||||||
|
width: 32px;
|
||||||
|
border-radius: 50%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
&.title {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
&.main {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat_msg_img {
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
border-radius: 50%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat_area_main{
|
||||||
|
overflow-y: scroll;
|
||||||
|
overflow-x: hidden;
|
||||||
|
padding: 30px 0 30px 0;
|
||||||
|
height: calc(100% - @chat_footer_height);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat_msg_profile {
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-top: auto;
|
||||||
|
margin-bottom: -20px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat_msg_date {
|
||||||
|
position: absolute;
|
||||||
|
left: calc(100% + 12px);
|
||||||
|
bottom: 0;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: @msg-date;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat_msg {
|
||||||
|
display: flex;
|
||||||
|
padding: 0 20px 45px;
|
||||||
|
|
||||||
|
.chat_msg_content {
|
||||||
|
margin-left: 12px;
|
||||||
|
max-width: 70%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat_msg_text {
|
||||||
|
background-color: @chat-text-bg;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 20px 20px 20px 0;
|
||||||
|
line-height: 1.5;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-top: 10px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
&.owner{
|
||||||
|
flex-direction: row-reverse;
|
||||||
|
.chat_msg_content {
|
||||||
|
margin-left: 0;
|
||||||
|
margin-right: 12px;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
.chat_msg_text {
|
||||||
|
background-color: @theme-color;
|
||||||
|
color: #fff;
|
||||||
|
border-radius: 20px 20px 0 20px;
|
||||||
|
}
|
||||||
|
.chat_msg_date {
|
||||||
|
left: auto;
|
||||||
|
right: calc(100% + 12px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.chat_msg_text {
|
||||||
|
color: @chat-text-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat_msg_text img {
|
||||||
|
max-width: 300px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat_area_footer {
|
||||||
|
display: flex;
|
||||||
|
border-top: 1px solid @border-color;
|
||||||
|
width: 100%;
|
||||||
|
padding: 10px 20px;
|
||||||
|
height: @chat_footer_height;
|
||||||
|
align-items: center;
|
||||||
|
background-color: @theme-bg-color;
|
||||||
|
position: sticky;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat_area_footer svg {
|
||||||
|
color: @settings-icon-color;
|
||||||
|
width: 20px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
color: @settings-icon-hover;
|
||||||
|
}
|
||||||
|
|
||||||
|
& + svg {
|
||||||
|
margin-left: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat_area_footer input {
|
||||||
|
border: none;
|
||||||
|
color: @body-color;
|
||||||
|
background-color: @input-bg;
|
||||||
|
padding: 12px;
|
||||||
|
border-radius: 6px;
|
||||||
|
font-size: 15px;
|
||||||
|
margin: 0 12px;
|
||||||
|
width: 100%;
|
||||||
|
&::placeholder {
|
||||||
|
color: @input-chat-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat_area_group {
|
||||||
|
flex-shrink: 0;
|
||||||
|
display: flex;
|
||||||
|
* {
|
||||||
|
border: 2px solid @theme-bg-color;
|
||||||
|
}
|
||||||
|
* + * {
|
||||||
|
margin-left: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
background-color: @button-bg-color;
|
||||||
|
color: @theme-color;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 780px) {
|
||||||
|
.conversation_area {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
// FOR LAYOUTS
|
// FOR LAYOUTS
|
||||||
@__Global_general_font_family: "Poppins", sans-serif;
|
@__Global_general_font_family: "Poppins", sans-serif;
|
||||||
|
|
||||||
@__Global_layout_backgroud: #F8F6F8;
|
@__Global_layout_backgroud: #F8F6F8;
|
||||||
@__Global_layout_color: #2d2d2d;
|
@__Global_layout_color: #2d2d2d;
|
||||||
@__Global_layout_border-rd: 27px 0 0 0;
|
@__Global_layout_border-rd: 27px 0 0 0;
|
||||||
@ -128,7 +129,7 @@ body {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
// .primary_layout_*
|
// .primary_layout_*
|
||||||
@primary_layout_backgroud: #2d2d2d;
|
@primary_layout_backgroud: #F8F6F8; // #2d2d2d;
|
||||||
|
|
||||||
@primary_layout_container_backgroud: @__Global_layout_backgroud;
|
@primary_layout_container_backgroud: @__Global_layout_backgroud;
|
||||||
@primary_layout_container_border-rd: @__Global_layout_border-rd;
|
@primary_layout_container_border-rd: @__Global_layout_border-rd;
|
||||||
@ -140,8 +141,8 @@ body {
|
|||||||
@secondary_wrapper_hidden_width: 22vw;
|
@secondary_wrapper_hidden_width: 22vw;
|
||||||
@secondary_wrapper_showFull_width: 94.2%;
|
@secondary_wrapper_showFull_width: 94.2%;
|
||||||
@secondary_wrapper_showHalf_width: 35vw;
|
@secondary_wrapper_showHalf_width: 35vw;
|
||||||
@secondary_container_bg_background: #201F23;
|
@secondary_container_bg_background: #F8F6F8;
|
||||||
@secondary_container_1_color: #fff;
|
@secondary_container_1_color: #201F23;
|
||||||
@secondary_container_2_color: #201F23;
|
@secondary_container_2_color: #201F23;
|
||||||
@secondary_container_2_backgroud: #fff;
|
@secondary_container_2_backgroud: #fff;
|
||||||
@secondary_container_1_btn_backgroud: #4c4c4c;
|
@secondary_container_1_btn_backgroud: #4c4c4c;
|
||||||
@ -152,7 +153,7 @@ body {
|
|||||||
@secondaty_container_2_padding: 20px 15px 15px 15px;
|
@secondaty_container_2_padding: 20px 15px 15px 15px;
|
||||||
|
|
||||||
// .left_sider*
|
// .left_sider*
|
||||||
@left_sider_backgroud: #2d2d2d;
|
@left_sider_backgroud: #F8F6F8;
|
||||||
@left_sider_color: #fff;
|
@left_sider_color: #fff;
|
||||||
@left_sider_sizeIcons: 19px;
|
@left_sider_sizeIcons: 19px;
|
||||||
@left_sider_menu__onhover_backgroud: rgb(80, 80, 80);
|
@left_sider_menu__onhover_backgroud: rgb(80, 80, 80);
|
||||||
@ -226,3 +227,41 @@ body {
|
|||||||
@borderRadius: 6px;
|
@borderRadius: 6px;
|
||||||
@boxShadow: 0 2px 5px rgba(#333, 0.2);
|
@boxShadow: 0 2px 5px rgba(#333, 0.2);
|
||||||
|
|
||||||
|
// DSP2
|
||||||
|
@chat_footer_height: 67px;
|
||||||
|
@body-bg-color: #e5ecef;
|
||||||
|
@theme-bg-color: #fff;
|
||||||
|
@settings-icon-hover: #9fa7ac;
|
||||||
|
@developer-color: #f9fafb;
|
||||||
|
@input-bg: #f8f8fa;
|
||||||
|
@input-chat-color: #a2a2a2;
|
||||||
|
@border-color: #eef2f4;
|
||||||
|
@body-font: "Manrope", sans-serif;
|
||||||
|
@body-color: #273346;
|
||||||
|
@settings-icon-color: #c1c7cd;
|
||||||
|
@msg-message: #969eaa;
|
||||||
|
@chat-text-bg: #f1f2f6;
|
||||||
|
@chat-text-color: rgb(39, 51, 70);
|
||||||
|
@theme-color: #0086ff;
|
||||||
|
@msg-date: #c0c7d2;
|
||||||
|
@button-bg-color: #f0f7ff;
|
||||||
|
@button-color: var(@theme-color);
|
||||||
|
@detail-font-color: #919ca2;
|
||||||
|
@msg-hover-bg: rgba(238, 242, 244, 0.4);
|
||||||
|
@active-conversation-bg: linear-gradient(
|
||||||
|
to right,
|
||||||
|
rgba(238, 242, 244, 0.4) 0%,
|
||||||
|
rgba(238, 242, 244, 0) 100%
|
||||||
|
);
|
||||||
|
@overlay-bg: linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
rgba(255, 255, 255, 0) 0%,
|
||||||
|
rgba(255, 255, 255, 1) 65%,
|
||||||
|
rgba(255, 255, 255, 1) 100%
|
||||||
|
);
|
||||||
|
@chat-header-bg: linear-gradient(
|
||||||
|
to bottom,
|
||||||
|
rgba(255, 255, 255, 1) 0%,
|
||||||
|
rgba(255, 255, 255, 1) 78%,
|
||||||
|
rgba(255, 255, 255, 0) 100%
|
||||||
|
);
|
@ -3,3 +3,4 @@
|
|||||||
@import url('https://fonts.googleapis.com/css?family=Source+Sans+Pro&display=swap');
|
@import url('https://fonts.googleapis.com/css?family=Source+Sans+Pro&display=swap');
|
||||||
@import url('https://fonts.googleapis.com/css?family=Kulim+Park&display=swap');
|
@import url('https://fonts.googleapis.com/css?family=Kulim+Park&display=swap');
|
||||||
@import url('https://fonts.googleapis.com/css?family=Nunito&display=swap');
|
@import url('https://fonts.googleapis.com/css?family=Nunito&display=swap');
|
||||||
|
@import url("https://fonts.googleapis.com/css?family=Manrope:300,400,500,600,700&display=swap&subset=latin-ext");
|
||||||
|
Loading…
x
Reference in New Issue
Block a user