Feature: Add online status to each user (#452)
* Feature: Add online status to each user * Add OnlineStatusable, add availability status to thumbnail
This commit is contained in:
@@ -1,5 +1,10 @@
|
|||||||
class RoomChannel < ApplicationCable::Channel
|
class RoomChannel < ApplicationCable::Channel
|
||||||
def subscribed
|
def subscribed
|
||||||
stream_from params[:pubsub_token]
|
stream_from params[:pubsub_token]
|
||||||
|
::OnlineStatusTracker.add_subscription(params[:pubsub_token])
|
||||||
|
end
|
||||||
|
|
||||||
|
def unsubscribed
|
||||||
|
::OnlineStatusTracker.remove_subscription(params[:pubsub_token])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ class Api::V1::AgentsController < Api::BaseController
|
|||||||
before_action :build_agent, only: [:create]
|
before_action :build_agent, only: [:create]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
render json: agents
|
@agents = agents
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy
|
def destroy
|
||||||
|
|||||||
@@ -16,12 +16,17 @@
|
|||||||
:size="avatarSize"
|
:size="avatarSize"
|
||||||
/>
|
/>
|
||||||
<img
|
<img
|
||||||
v-if="badge === 'Channel::FacebookPage'"
|
v-if="badge === 'Channel::FacebookPage' && status !== ''"
|
||||||
id="badge"
|
id="badge"
|
||||||
class="source-badge"
|
class="source-badge"
|
||||||
:style="badgeStyle"
|
:style="badgeStyle"
|
||||||
src="~dashboard/assets/images/fb-badge.png"
|
src="~dashboard/assets/images/fb-badge.png"
|
||||||
/>
|
/>
|
||||||
|
<div
|
||||||
|
v-else-if="status === 'online'"
|
||||||
|
class="source-badge user--online"
|
||||||
|
:style="statusStyle"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
@@ -41,6 +46,7 @@ export default {
|
|||||||
props: {
|
props: {
|
||||||
src: {
|
src: {
|
||||||
type: String,
|
type: String,
|
||||||
|
default: '',
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -52,6 +58,11 @@ export default {
|
|||||||
},
|
},
|
||||||
username: {
|
username: {
|
||||||
type: String,
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
@@ -67,6 +78,10 @@ export default {
|
|||||||
const badgeSize = `${this.avatarSize / 3}px`;
|
const badgeSize = `${this.avatarSize / 3}px`;
|
||||||
return { width: badgeSize, height: badgeSize };
|
return { width: badgeSize, height: badgeSize };
|
||||||
},
|
},
|
||||||
|
statusStyle() {
|
||||||
|
const statusSize = `${this.avatarSize / 4}px`;
|
||||||
|
return { width: statusSize, height: statusSize };
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
onImgError() {
|
onImgError() {
|
||||||
@@ -78,6 +93,7 @@ export default {
|
|||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '~dashboard/assets/scss/variables';
|
@import '~dashboard/assets/scss/variables';
|
||||||
|
@import '~dashboard/assets/scss/foundation-settings';
|
||||||
@import '~dashboard/assets/scss/mixins';
|
@import '~dashboard/assets/scss/mixins';
|
||||||
|
|
||||||
.user-thumbnail-box {
|
.user-thumbnail-box {
|
||||||
@@ -91,11 +107,21 @@ export default {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.source-badge {
|
.source-badge {
|
||||||
bottom: -$space-micro / 2;
|
bottom: -$space-micro;
|
||||||
height: $space-slab;
|
height: $space-slab;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: $zero;
|
right: $zero;
|
||||||
width: $space-slab;
|
width: $space-slab;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.user--online {
|
||||||
|
background: $success-color;
|
||||||
|
border-radius: 50%;
|
||||||
|
bottom: $space-micro;
|
||||||
|
|
||||||
|
&:after {
|
||||||
|
content: ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
size="56px"
|
size="56px"
|
||||||
:badge="contact.channel"
|
:badge="contact.channel"
|
||||||
:username="contact.name"
|
:username="contact.name"
|
||||||
|
:status="contact.availability_status"
|
||||||
/>
|
/>
|
||||||
<div class="contact--details">
|
<div class="contact--details">
|
||||||
<div class="contact--name">
|
<div class="contact--name">
|
||||||
|
|||||||
@@ -26,10 +26,11 @@
|
|||||||
<!-- Gravtar Image -->
|
<!-- Gravtar Image -->
|
||||||
<td>
|
<td>
|
||||||
<thumbnail
|
<thumbnail
|
||||||
:src="gravatarUrl(agent.email)"
|
:src="agent.thumbnail"
|
||||||
class="columns"
|
class="columns"
|
||||||
:username="agent.name"
|
:username="agent.name"
|
||||||
size="40px"
|
size="40px"
|
||||||
|
:status="agent.availability_status"
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
<!-- Agent Name + Email -->
|
<!-- Agent Name + Email -->
|
||||||
|
|||||||
@@ -49,7 +49,6 @@ export const getters = {
|
|||||||
Object.values(_state.conversations),
|
Object.values(_state.conversations),
|
||||||
message => new DateHelper(message.created_at).format()
|
message => new DateHelper(message.created_at).format()
|
||||||
);
|
);
|
||||||
|
|
||||||
return Object.keys(conversationGroupedByDate).map(date => {
|
return Object.keys(conversationGroupedByDate).map(date => {
|
||||||
const messages = conversationGroupedByDate[date].map((message, index) => {
|
const messages = conversationGroupedByDate[date].map((message, index) => {
|
||||||
let showAvatar = false;
|
let showAvatar = false;
|
||||||
@@ -59,12 +58,11 @@ export const getters = {
|
|||||||
const nextMessage = conversationGroupedByDate[date][index + 1];
|
const nextMessage = conversationGroupedByDate[date][index + 1];
|
||||||
const currentSender = message.sender ? message.sender.name : '';
|
const currentSender = message.sender ? message.sender.name : '';
|
||||||
const nextSender = nextMessage.sender ? nextMessage.sender.name : '';
|
const nextSender = nextMessage.sender ? nextMessage.sender.name : '';
|
||||||
showAvatar = currentSender !== nextSender;
|
showAvatar =
|
||||||
|
currentSender !== nextSender ||
|
||||||
|
message.message_type !== nextMessage.message_type;
|
||||||
}
|
}
|
||||||
return {
|
return { showAvatar, ...message };
|
||||||
showAvatar,
|
|
||||||
...message,
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -61,19 +61,27 @@ describe('#getters', () => {
|
|||||||
id: 1,
|
id: 1,
|
||||||
content: 'Thanks for the help',
|
content: 'Thanks for the help',
|
||||||
created_at: 1574075964,
|
created_at: 1574075964,
|
||||||
|
message_type: 0,
|
||||||
},
|
},
|
||||||
2: {
|
2: {
|
||||||
id: 2,
|
id: 2,
|
||||||
content: 'Yes, It makes sense',
|
content: 'Yes, It makes sense',
|
||||||
created_at: 1574092218,
|
created_at: 1574092218,
|
||||||
|
message_type: 0,
|
||||||
},
|
},
|
||||||
3: {
|
3: {
|
||||||
id: 3,
|
id: 3,
|
||||||
content: 'Hey',
|
content: 'Hey',
|
||||||
created_at: 1576340623,
|
created_at: 1574092218,
|
||||||
|
message_type: 1,
|
||||||
},
|
},
|
||||||
4: {
|
4: {
|
||||||
id: 4,
|
id: 4,
|
||||||
|
content: 'Hey',
|
||||||
|
created_at: 1576340623,
|
||||||
|
},
|
||||||
|
5: {
|
||||||
|
id: 5,
|
||||||
content: 'How may I help you',
|
content: 'How may I help you',
|
||||||
created_at: 1576340626,
|
created_at: 1576340626,
|
||||||
},
|
},
|
||||||
@@ -88,12 +96,21 @@ describe('#getters', () => {
|
|||||||
content: 'Thanks for the help',
|
content: 'Thanks for the help',
|
||||||
created_at: 1574075964,
|
created_at: 1574075964,
|
||||||
showAvatar: false,
|
showAvatar: false,
|
||||||
|
message_type: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 2,
|
id: 2,
|
||||||
content: 'Yes, It makes sense',
|
content: 'Yes, It makes sense',
|
||||||
created_at: 1574092218,
|
created_at: 1574092218,
|
||||||
showAvatar: true,
|
showAvatar: true,
|
||||||
|
message_type: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
content: 'Hey',
|
||||||
|
created_at: 1574092218,
|
||||||
|
showAvatar: true,
|
||||||
|
message_type: 1,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -101,13 +118,13 @@ describe('#getters', () => {
|
|||||||
date: 'Dec 14, 2019',
|
date: 'Dec 14, 2019',
|
||||||
messages: [
|
messages: [
|
||||||
{
|
{
|
||||||
id: 3,
|
id: 4,
|
||||||
content: 'Hey',
|
content: 'Hey',
|
||||||
created_at: 1576340623,
|
created_at: 1576340623,
|
||||||
showAvatar: false,
|
showAvatar: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 4,
|
id: 5,
|
||||||
content: 'How may I help you',
|
content: 'How may I help you',
|
||||||
created_at: 1576340626,
|
created_at: 1576340626,
|
||||||
showAvatar: true,
|
showAvatar: true,
|
||||||
|
|||||||
11
app/models/concerns/availability_statusable.rb
Normal file
11
app/models/concerns/availability_statusable.rb
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
module AvailabilityStatusable
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
def online?
|
||||||
|
::OnlineStatusTracker.subscription_count(pubsub_token) != 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def availability_status
|
||||||
|
online? ? 'online' : 'offline'
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
class Contact < ApplicationRecord
|
class Contact < ApplicationRecord
|
||||||
include Pubsubable
|
include Pubsubable
|
||||||
include Avatarable
|
include Avatarable
|
||||||
|
include AvailabilityStatusable
|
||||||
validates :account_id, presence: true
|
validates :account_id, presence: true
|
||||||
|
|
||||||
belongs_to :account
|
belongs_to :account
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ class User < ApplicationRecord
|
|||||||
include Events::Types
|
include Events::Types
|
||||||
include Pubsubable
|
include Pubsubable
|
||||||
include Avatarable
|
include Avatarable
|
||||||
|
include AvailabilityStatusable
|
||||||
include Rails.application.routes.url_helpers
|
include Rails.application.routes.url_helpers
|
||||||
|
|
||||||
devise :database_authenticatable,
|
devise :database_authenticatable,
|
||||||
|
|||||||
10
app/views/api/v1/agents/index.json.jbuilder
Normal file
10
app/views/api/v1/agents/index.json.jbuilder
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
json.array! @agents do |agent|
|
||||||
|
json.account_id agent.account_id
|
||||||
|
json.availability_status agent.availability_status
|
||||||
|
json.confirmed agent.confirmed?
|
||||||
|
json.email agent.email
|
||||||
|
json.id agent.id
|
||||||
|
json.name agent.name
|
||||||
|
json.role agent.role
|
||||||
|
json.thumbnail agent.avatar_url
|
||||||
|
end
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
json.payload do
|
json.payload do
|
||||||
|
json.availability_status @contact.availability_status
|
||||||
|
json.email @contact.email
|
||||||
json.id @contact.id
|
json.id @contact.id
|
||||||
json.name @contact.name
|
json.name @contact.name
|
||||||
json.email @contact.email
|
|
||||||
json.phone_number @contact.phone_number
|
json.phone_number @contact.phone_number
|
||||||
json.thumbnail @contact.avatar_url
|
json.thumbnail @contact.avatar_url
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,5 +2,6 @@ json.payload do
|
|||||||
json.array! @inbox_members do |inbox_member|
|
json.array! @inbox_members do |inbox_member|
|
||||||
json.name inbox_member.user.name
|
json.name inbox_member.user.name
|
||||||
json.avatar_url inbox_member.user.avatar_url
|
json.avatar_url inbox_member.user.avatar_url
|
||||||
|
json.availability_status inbox_member.user.availability_status
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
19
lib/online_status_tracker.rb
Normal file
19
lib/online_status_tracker.rb
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
module OnlineStatusTracker
|
||||||
|
def self.add_subscription(channel_id)
|
||||||
|
count = subscription_count(channel_id)
|
||||||
|
::Redis::Alfred.setex(channel_id, count + 1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.remove_subscription(channel_id)
|
||||||
|
count = subscription_count(channel_id)
|
||||||
|
if count == 1
|
||||||
|
::Redis::Alfred.delete(channel_id)
|
||||||
|
elsif count != 0
|
||||||
|
::Redis::Alfred.setex(channel_id, count - 1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.subscription_count(channel_id)
|
||||||
|
::Redis::Alfred.get(channel_id).to_i
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user