Skip to content

Commit

Permalink
Feature: Availability Statuses (#874)
Browse files Browse the repository at this point in the history
Co-authored-by: Pranav Raj S <[email protected]>
  • Loading branch information
sojan-official and Pranav Raj S authored Jul 4, 2020
1 parent bd87927 commit c98907d
Show file tree
Hide file tree
Showing 35 changed files with 413 additions and 77 deletions.
40 changes: 36 additions & 4 deletions app/channels/room_channel.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,42 @@
class RoomChannel < ApplicationCable::Channel
def subscribed
stream_from params[:pubsub_token]
::OnlineStatusTracker.add_subscription(params[:pubsub_token])
ensure_stream
current_user
current_account
update_subscription
end

def unsubscribed
::OnlineStatusTracker.remove_subscription(params[:pubsub_token])
def update_presence
update_subscription
data = { account_id: @current_account.id, users: ::OnlineStatusTracker.get_available_users(@current_account.id) }
data[:contacts] = ::OnlineStatusTracker.get_available_contacts(@current_account.id) if @current_user.is_a? User
ActionCable.server.broadcast(@pubsub_token, { event: 'presence.update', data: data })
end

private

def ensure_stream
@pubsub_token = params[:pubsub_token]
stream_from @pubsub_token
end

def update_subscription
::OnlineStatusTracker.update_presence(@current_account.id, @current_user.class.name, @current_user.id)
end

def current_user
@current_user ||= if params[:user_id].blank?
Contact.find_by!(pubsub_token: @pubsub_token)
else
User.find_by!(pubsub_token: @pubsub_token, id: params[:user_id])
end
end

def current_account
@current_account ||= if @current_user.is_a? Contact
@current_user.account
else
@current_user.accounts.find(params[:account_id])
end
end
end
2 changes: 1 addition & 1 deletion app/controllers/api/v1/profiles_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@ def set_user
end

def profile_params
params.require(:profile).permit(:email, :name, :password, :password_confirmation, :avatar)
params.require(:profile).permit(:email, :name, :password, :password_confirmation, :avatar, :availability)
end
end
17 changes: 7 additions & 10 deletions app/javascript/dashboard/api/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,21 +118,18 @@ export default {
return axios.post(urlData.url, { email });
},

profileUpdate({ name, email, password, password_confirmation, avatar }) {
profileUpdate({ password, password_confirmation, ...profileAttributes }) {
const formData = new FormData();
if (name) {
formData.append('profile[name]', name);
}
if (email) {
formData.append('profile[email]', email);
}
Object.keys(profileAttributes).forEach(key => {
const value = profileAttributes[key];
if (value) {
formData.append(`profile[${key}]`, value);
}
});
if (password && password_confirmation) {
formData.append('profile[password]', password);
formData.append('profile[password_confirmation]', password_confirmation);
}
if (avatar) {
formData.append('profile[avatar]', avatar);
}
return axios.put(endPoints('profileUpdate').url, formData);
},
};
6 changes: 5 additions & 1 deletion app/javascript/dashboard/components/layout/Sidebar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,11 @@
</div>
</transition>
<div class="current-user" @click.prevent="showOptions()">
<thumbnail :src="currentUser.avatar_url" :username="currentUser.name" />
<thumbnail
:src="currentUser.avatar_url"
:username="currentUser.name"
:status="currentUser.availability_status"
/>
<div class="current-user--data">
<h3 class="current-user--name">
{{ currentUser.name }}
Expand Down
25 changes: 17 additions & 8 deletions app/javascript/dashboard/components/widgets/Thumbnail.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,26 +21,25 @@
:style="badgeStyle"
src="~dashboard/assets/images/fb-badge.png"
/>
<div
v-else-if="status === 'online'"
class="source-badge user--online"
:style="statusStyle"
></div>
<img
v-if="badge === 'Channel::TwitterProfile'"
id="badge"
class="source-badge"
:style="badgeStyle"
src="~dashboard/assets/images/twitter-badge.png"
/>

<img
v-if="badge === 'Channel::TwilioSms'"
id="badge"
class="source-badge"
:style="badgeStyle"
src="~dashboard/assets/images/channels/whatsapp.png"
/>
<div
v-if="showStatusIndicator"
:class="`source-badge user-online-status user-online-status--${status}`"
:style="statusStyle"
/>
</div>
</template>
<script>
Expand Down Expand Up @@ -89,6 +88,9 @@ export default {
};
},
computed: {
showStatusIndicator() {
return this.status === 'online' || this.status === 'busy';
},
avatarSize() {
return Number(this.size.replace(/\D+/g, ''));
},
Expand Down Expand Up @@ -150,14 +152,21 @@ export default {
width: $space-slab;
}
.user--online {
background: $success-color;
.user-online-status {
border-radius: 50%;
bottom: $space-micro;
&:after {
content: ' ';
}
}
.user-online-status--online {
background: $success-color;
}
.user-online-status--busy {
background: $warning-color;
}
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
:badge="currentContact.channel"
class="columns"
:username="currentContact.name"
:status="currentContact.availability_status"
size="40px"
/>
<div class="conversation--details columns">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
size="40px"
:badge="currentContact.channel"
:username="currentContact.name"
:status="currentContact.availability_status"
/>
<div class="user--profile__meta">
<h3 v-if="!isContactPanelOpen" class="user--name text-truncate">
Expand Down
6 changes: 6 additions & 0 deletions app/javascript/dashboard/helper/actionCable.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class ActionCableConnector extends BaseActionCableConnector {
'conversation.typing_on': this.onTypingOn,
'conversation.typing_off': this.onTypingOff,
'conversation.contact_changed': this.onConversationContactChange,
'presence.update': this.onPresenceUpdate,
};
}

Expand All @@ -29,6 +30,11 @@ class ActionCableConnector extends BaseActionCableConnector {
this.app.$store.dispatch('updateMessage', data);
};

onPresenceUpdate = data => {
this.app.$store.dispatch('contacts/updatePresence', data.contacts);
this.app.$store.dispatch('agents/updatePresence', data.users);
};

onConversationContactChange = payload => {
const { meta = {}, id: conversationId } = payload;
const { sender } = meta || {};
Expand Down
17 changes: 17 additions & 0 deletions app/javascript/dashboard/i18n/locale/en/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,23 @@
"ERROR": "Please enter a valid name",
"PLACEHOLDER": "Please enter your name, this would be displayed in conversations"
},
"AVAILABILITY": {
"LABEL": "Availability",
"STATUSES_LIST": [
{
"value": "online",
"label": "Online"
},
{
"value": "busy",
"label": "Busy"
},
{
"value": "offline",
"label": "Offline"
}
]
},
"EMAIL": {
"LABEL": "Your email address",
"ERROR": "Please enter a valid email address",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,19 @@
{{ $t('PROFILE_SETTINGS.FORM.EMAIL.ERROR') }}
</span>
</label>
<label>
{{ $t('PROFILE_SETTINGS.FORM.AVAILABILITY.LABEL') }}
<select v-model="availability">
<option
v-for="status in availabilityStatuses"
:key="status.key"
class="text-capitalize"
:value="status.value"
>
{{ status.label }}
</option>
</select>
</label>
</div>
</div>
<div class="profile--settings--row row">
Expand Down Expand Up @@ -99,16 +112,17 @@
</template>

<script>
/* global bus */
import { required, minLength, email } from 'vuelidate/lib/validators';
import { mapGetters } from 'vuex';
import { clearCookiesOnLogout } from '../../../../store/utils/api';
import NotificationSettings from './NotificationSettings';
import alertMixin from 'shared/mixins/alertMixin';
export default {
components: {
NotificationSettings,
},
mixin: [alertMixin],
data() {
return {
avatarFile: '',
Expand All @@ -117,7 +131,11 @@ export default {
email: '',
password: '',
passwordConfirmation: '',
availability: 'online',
isUpdating: false,
availabilityStatuses: this.$t(
'PROFILE_SETTINGS.FORM.AVAILABILITY.STATUSES_LIST'
),
};
},
validations: {
Expand Down Expand Up @@ -164,11 +182,12 @@ export default {
this.name = this.currentUser.name;
this.email = this.currentUser.email;
this.avatarUrl = this.currentUser.avatar_url;
this.availability = this.currentUser.availability_status;
},
async updateUser() {
this.$v.$touch();
if (this.$v.$invalid) {
bus.$emit('newToastMessage', this.$t('PROFILE_SETTINGS.FORM.ERROR'));
this.showAlert(this.$t('PROFILE_SETTINGS.FORM.ERROR'));
return;
}
this.isUpdating = true;
Expand All @@ -179,15 +198,13 @@ export default {
email: this.email,
avatar: this.avatarFile,
password: this.password,
availability: this.availability,
password_confirmation: this.passwordConfirmation,
});
this.isUpdating = false;
if (hasEmailChanged) {
clearCookiesOnLogout();
bus.$emit(
'newToastMessage',
this.$t('PROFILE_SETTINGS.AFTER_EMAIL_CHANGED')
);
this.showAlert(this.$t('PROFILE_SETTINGS.AFTER_EMAIL_CHANGED'));
}
} catch (error) {
this.isUpdating = false;
Expand Down
8 changes: 8 additions & 0 deletions app/javascript/dashboard/store/modules/agents.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,13 @@ export const actions = {
throw new Error(error);
}
},

updatePresence: async ({ commit }, data) => {
commit(types.default.SET_AGENT_UPDATING_STATUS, true);
commit(types.default.UPDATE_AGENTS_PRESENCE, data);
commit(types.default.SET_AGENT_UPDATING_STATUS, false);
},

delete: async ({ commit }, agentId) => {
commit(types.default.SET_AGENT_DELETING_STATUS, true);
try {
Expand Down Expand Up @@ -88,6 +95,7 @@ export const mutations = {
[types.default.ADD_AGENT]: MutationHelpers.create,
[types.default.EDIT_AGENT]: MutationHelpers.update,
[types.default.DELETE_AGENT]: MutationHelpers.destroy,
[types.default.UPDATE_AGENTS_PRESENCE]: MutationHelpers.updatePresence,
};

export default {
Expand Down
19 changes: 19 additions & 0 deletions app/javascript/dashboard/store/modules/contacts.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ export const actions = {
throw new Error(error);
}
},

updatePresence: ({ commit }, data) => {
commit(types.default.UPDATE_CONTACTS_PRESENCE, data);
},
};

export const mutations = {
Expand Down Expand Up @@ -88,6 +92,21 @@ export const mutations = {
[types.default.EDIT_CONTACT]: ($state, data) => {
Vue.set($state.records, data.id, data);
},

[types.default.UPDATE_CONTACTS_PRESENCE]: ($state, data) => {
Object.values($state.records).forEach(element => {
const availabilityStatus = data[element.id];
if (availabilityStatus) {
Vue.set(
$state.records[element.id],
'availability_status',
availabilityStatus
);
} else {
Vue.delete($state.records[element.id], 'availability_status');
}
});
},
};

export default {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,16 @@ describe('#actions', () => {
]);
});
});

describe('#updatePresence', () => {
it('sends correct actions if API is success', async () => {
const data = { users: { 1: 'online' }, contacts: { 2: 'online' } };
actions.updatePresence({ commit }, data);
expect(commit.mock.calls).toEqual([
[types.default.SET_AGENT_UPDATING_STATUS, true],
[types.default.UPDATE_AGENTS_PRESENCE, data],
[types.default.SET_AGENT_UPDATING_STATUS, false],
]);
});
});
});
Loading

0 comments on commit c98907d

Please sign in to comment.