From 6c0fefad152e1843bccf80fb675301b789f70dd5 Mon Sep 17 00:00:00 2001 From: JoramWilander Date: Mon, 10 Aug 2015 14:47:45 -0400 Subject: added getChannelCounts service and refactored the client to more intelligently pull channel data --- api/channel.go | 17 ++ model/channel_count.go | 46 +++++ store/sql_channel_store.go | 33 ++++ store/store.go | 1 + web/react/components/more_channels.jsx | 123 +++++++------ web/react/components/new_channel.jsx | 4 +- web/react/components/rename_channel_modal.jsx | 2 +- web/react/components/sidebar.jsx | 9 +- web/react/utils/async_client.jsx | 246 +++++++++++++++----------- web/react/utils/client.jsx | 24 ++- 10 files changed, 340 insertions(+), 165 deletions(-) create mode 100644 model/channel_count.go diff --git a/api/channel.go b/api/channel.go index a3de30377..851816dde 100644 --- a/api/channel.go +++ b/api/channel.go @@ -18,6 +18,7 @@ func InitChannel(r *mux.Router) { sr := r.PathPrefix("/channels").Subrouter() sr.Handle("/", ApiUserRequiredActivity(getChannels, false)).Methods("GET") sr.Handle("/more", ApiUserRequired(getMoreChannels)).Methods("GET") + sr.Handle("/counts", ApiUserRequiredActivity(getChannelCounts, false)).Methods("GET") sr.Handle("/create", ApiUserRequired(createChannel)).Methods("POST") sr.Handle("/create_direct", ApiUserRequired(createDirectChannel)).Methods("POST") sr.Handle("/update", ApiUserRequired(updateChannel)).Methods("POST") @@ -315,6 +316,22 @@ func getMoreChannels(c *Context, w http.ResponseWriter, r *http.Request) { } } +func getChannelCounts(c *Context, w http.ResponseWriter, r *http.Request) { + + // user is already in the team + + if result := <-Srv.Store.Channel().GetChannelCounts(c.Session.TeamId, c.Session.UserId); result.Err != nil { + c.Err = model.NewAppError("getChannelCounts", "Unable to get channel counts from the database", result.Err.Message) + return + } else if HandleEtag(result.Data.(*model.ChannelCounts).Etag(), w, r) { + return + } else { + data := result.Data.(*model.ChannelCounts) + w.Header().Set(model.HEADER_ETAG_SERVER, data.Etag()) + w.Write([]byte(data.ToJson())) + } +} + func joinChannel(c *Context, w http.ResponseWriter, r *http.Request) { params := mux.Vars(r) diff --git a/model/channel_count.go b/model/channel_count.go new file mode 100644 index 000000000..05d8401e1 --- /dev/null +++ b/model/channel_count.go @@ -0,0 +1,46 @@ +// Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. +// See License.txt for license information. + +package model + +import ( + "crypto/md5" + "encoding/json" + "io" + "strconv" +) + +type ChannelCounts struct { + Counts map[string]int64 `json:"counts"` +} + +func (o *ChannelCounts) Etag() string { + str := "" + for id, count := range o.Counts { + str += id + strconv.FormatInt(count, 10) + } + + data := []byte(str) + + return Etag(md5.Sum(data)) +} + +func (o *ChannelCounts) ToJson() string { + b, err := json.Marshal(o) + if err != nil { + return "" + } else { + return string(b) + } +} + +func ChannelCountsFromJson(data io.Reader) *ChannelCounts { + decoder := json.NewDecoder(data) + var o ChannelCounts + err := decoder.Decode(&o) + if err == nil { + return &o + } else { + return nil + } +} diff --git a/store/sql_channel_store.go b/store/sql_channel_store.go index cac5c681b..6caa6fc70 100644 --- a/store/sql_channel_store.go +++ b/store/sql_channel_store.go @@ -281,6 +281,39 @@ func (s SqlChannelStore) GetMoreChannels(teamId string, userId string) StoreChan return storeChannel } +type channelIdWithCount struct { + Id string + TotalMsgCount int64 +} + +func (s SqlChannelStore) GetChannelCounts(teamId string, userId string) StoreChannel { + storeChannel := make(StoreChannel) + + go func() { + result := StoreResult{} + + var data []channelIdWithCount + _, err := s.GetReplica().Select(&data, "SELECT Id, TotalMsgCount FROM Channels WHERE Id IN (SELECT ChannelId FROM ChannelMembers WHERE UserId = :UserId) AND TeamId = :TeamId AND DeleteAt = 0 ORDER BY DisplayName", map[string]interface{}{"TeamId": teamId, "UserId": userId}) + + if err != nil { + result.Err = model.NewAppError("SqlChannelStore.GetChannelCounts", "We couldn't get the channel counts", "teamId="+teamId+", userId="+userId+", err="+err.Error()) + } else { + counts := &model.ChannelCounts{Counts: make(map[string]int64)} + for i := range data { + v := data[i] + counts.Counts[v.Id] = v.TotalMsgCount + } + + result.Data = counts + } + + storeChannel <- result + close(storeChannel) + }() + + return storeChannel +} + func (s SqlChannelStore) GetByName(teamId string, name string) StoreChannel { storeChannel := make(StoreChannel) diff --git a/store/store.go b/store/store.go index 0934fe84b..613fe4198 100644 --- a/store/store.go +++ b/store/store.go @@ -50,6 +50,7 @@ type ChannelStore interface { GetByName(team_id string, domain string) StoreChannel GetChannels(teamId string, userId string) StoreChannel GetMoreChannels(teamId string, userId string) StoreChannel + GetChannelCounts(teamId string, userId string) StoreChannel SaveMember(member *model.ChannelMember) StoreChannel GetMembers(channelId string) StoreChannel diff --git a/web/react/components/more_channels.jsx b/web/react/components/more_channels.jsx index 007476f9b..e851283ae 100644 --- a/web/react/components/more_channels.jsx +++ b/web/react/components/more_channels.jsx @@ -1,34 +1,32 @@ // Copyright (c) 2015 Spinpunch, Inc. All Rights Reserved. // See License.txt for license information. - var utils = require('../utils/utils.jsx'); var client = require('../utils/client.jsx'); var asyncClient = require('../utils/async_client.jsx'); -var UserStore = require('../stores/user_store.jsx'); var ChannelStore = require('../stores/channel_store.jsx'); var LoadingScreen = require('./loading_screen.jsx'); function getStateFromStores() { - return { - channels: ChannelStore.getMoreAll(), - server_error: null - }; + return { + channels: ChannelStore.getMoreAll(), + serverError: null + }; } module.exports = React.createClass({ - displayName: "MoreChannelsModal", + displayName: 'MoreChannelsModal', componentDidMount: function() { ChannelStore.addMoreChangeListener(this._onChange); - $(this.refs.modal.getDOMNode()).on('shown.bs.modal', function (e) { + $(this.refs.modal.getDOMNode()).on('shown.bs.modal', function shown() { asyncClient.getMoreChannels(true); }); var self = this; - $(this.refs.modal.getDOMNode()).on('show.bs.modal', function(e) { + $(this.refs.modal.getDOMNode()).on('show.bs.modal', function show(e) { var button = e.relatedTarget; - self.setState({ channel_type: $(button).attr('data-channeltype') }); + self.setState({channelType: $(button).attr('data-channeltype')}); }); }, componentWillUnmount: function() { @@ -42,18 +40,17 @@ module.exports = React.createClass({ }, getInitialState: function() { var initState = getStateFromStores(); - initState.channel_type = ""; + initState.channelType = ''; return initState; }, - handleJoin: function(e) { - var self = this; - client.joinChannel(e, - function(data) { - $(self.refs.modal.getDOMNode()).modal('hide'); - asyncClient.getChannels(true); + handleJoin: function(id) { + client.joinChannel(id, + function() { + $(this.refs.modal.getDOMNode()).modal('hide'); + asyncClient.getChannel(id); }.bind(this), function(err) { - this.state.server_error = err.message; + this.state.serverError = err.message; this.setState(this.state); }.bind(this) ); @@ -62,52 +59,66 @@ module.exports = React.createClass({ $(this.refs.modal.getDOMNode()).modal('hide'); }, render: function() { - var server_error = this.state.server_error ?
: null; + var serverError; + if (this.state.serverError) { + serverError =
; + } + var outter = this; var moreChannels; - if (this.state.channels != null) - moreChannels = this.state.channels; + if (this.state.channels != null) { + var channels = this.state.channels; + if (!channels.loading) { + if (channels.length) { + moreChannels = ( + + + {moreChannels.map(function cMap(channel) { + return ( + + + + + ); + })} + +
+

{channel.display_name}

+

{channel.description}

+
+ ); + } else { + moreChannels = ( +
+

No more channels to join

+

Click 'Create New Channel' to make a new one

+
+ ); + } + } else { + moreChannels = ; + } + } return ( -