mirror of
https://github.com/mattermost/mattermost.git
synced 2025-02-25 18:55:24 -06:00
MM-32655 - Collapsed threads websocket handling (#16909)
Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
This commit is contained in:
@@ -2945,13 +2945,13 @@ func updateReadStateThreadByUser(c *Context, w http.ResponseWriter, r *http.Requ
|
||||
return
|
||||
}
|
||||
|
||||
err := c.App.UpdateThreadReadForUser(c.Params.UserId, c.Params.TeamId, c.Params.ThreadId, c.Params.Timestamp)
|
||||
thread, err := c.App.UpdateThreadReadForUser(c.Params.UserId, c.Params.TeamId, c.Params.ThreadId, c.Params.Timestamp)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
}
|
||||
|
||||
ReturnStatusOK(w)
|
||||
w.Write([]byte(thread.ToJson()))
|
||||
|
||||
auditRec.Success()
|
||||
}
|
||||
@@ -2973,7 +2973,7 @@ func unfollowThreadByUser(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
err := c.App.UpdateThreadFollowForUser(c.Params.UserId, c.Params.ThreadId, false)
|
||||
err := c.App.UpdateThreadFollowForUser(c.Params.UserId, c.Params.TeamId, c.Params.ThreadId, false)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
@@ -3001,7 +3001,7 @@ func followThreadByUser(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
err := c.App.UpdateThreadFollowForUser(c.Params.UserId, c.Params.ThreadId, true)
|
||||
err := c.App.UpdateThreadFollowForUser(c.Params.UserId, c.Params.TeamId, c.Params.ThreadId, true)
|
||||
if err != nil {
|
||||
c.Err = err
|
||||
return
|
||||
|
||||
@@ -5475,10 +5475,13 @@ func TestThreadSocketEvents(t *testing.T) {
|
||||
case ev := <-userWSClient.EventChannel:
|
||||
if ev.EventType() == model.WEBSOCKET_EVENT_THREAD_UPDATED {
|
||||
caught = true
|
||||
thread, err := model.ThreadFromJson(ev.GetData()["thread"].(string))
|
||||
thread, err := model.ThreadResponseFromJson(ev.GetData()["thread"].(string))
|
||||
require.NoError(t, err)
|
||||
require.Contains(t, thread.Participants, th.BasicUser.Id)
|
||||
require.Contains(t, thread.Participants, th.BasicUser2.Id)
|
||||
for _, p := range thread.Participants {
|
||||
if p.Id != th.BasicUser.Id && p.Id != th.BasicUser2.Id {
|
||||
require.Fail(t, "invalid participants")
|
||||
}
|
||||
}
|
||||
}
|
||||
case <-time.After(1 * time.Second):
|
||||
return
|
||||
@@ -5510,7 +5513,7 @@ func TestThreadSocketEvents(t *testing.T) {
|
||||
require.Truef(t, caught, "User should have received %s event", model.WEBSOCKET_EVENT_THREAD_FOLLOW_CHANGED)
|
||||
})
|
||||
|
||||
resp = th.Client.UpdateThreadReadForUser(th.BasicUser.Id, th.BasicTeam.Id, rpost.Id, 123)
|
||||
_, resp = th.Client.UpdateThreadReadForUser(th.BasicUser.Id, th.BasicTeam.Id, rpost.Id, 123)
|
||||
CheckNoError(t, resp)
|
||||
CheckOKStatus(t, resp)
|
||||
|
||||
@@ -5650,7 +5653,7 @@ func TestMaintainUnreadRepliesInThread(t *testing.T) {
|
||||
checkThreadListReplies(t, th, th.Client, th.BasicUser.Id, 0, 1, nil)
|
||||
|
||||
// mark other user's read state
|
||||
resp = th.SystemAdminClient.UpdateThreadReadForUser(th.SystemAdminUser.Id, th.BasicTeam.Id, rpost.Id, model.GetMillis())
|
||||
_, resp = th.SystemAdminClient.UpdateThreadReadForUser(th.SystemAdminUser.Id, th.BasicTeam.Id, rpost.Id, model.GetMillis())
|
||||
CheckNoError(t, resp)
|
||||
CheckOKStatus(t, resp)
|
||||
|
||||
@@ -5658,7 +5661,7 @@ func TestMaintainUnreadRepliesInThread(t *testing.T) {
|
||||
checkThreadListReplies(t, th, th.SystemAdminClient, th.SystemAdminUser.Id, 0, 0, &model.GetUserThreadsOpts{Unread: true})
|
||||
|
||||
// restore unread to an old date
|
||||
resp = th.SystemAdminClient.UpdateThreadReadForUser(th.SystemAdminUser.Id, th.BasicTeam.Id, rpost.Id, 123)
|
||||
_, resp = th.SystemAdminClient.UpdateThreadReadForUser(th.SystemAdminUser.Id, th.BasicTeam.Id, rpost.Id, 123)
|
||||
CheckNoError(t, resp)
|
||||
CheckOKStatus(t, resp)
|
||||
|
||||
@@ -5876,7 +5879,7 @@ func TestReadThreads(t *testing.T) {
|
||||
|
||||
uss, _ := checkThreadListReplies(t, th, th.Client, th.BasicUser.Id, 2, 2, nil)
|
||||
|
||||
resp := th.Client.UpdateThreadReadForUser(th.BasicUser.Id, th.BasicTeam.Id, rrpost.Id, model.GetMillis()+10)
|
||||
_, resp := th.Client.UpdateThreadReadForUser(th.BasicUser.Id, th.BasicTeam.Id, rrpost.Id, model.GetMillis()+10)
|
||||
CheckNoError(t, resp)
|
||||
CheckOKStatus(t, resp)
|
||||
|
||||
@@ -5884,7 +5887,7 @@ func TestReadThreads(t *testing.T) {
|
||||
require.Greater(t, uss2.Threads[0].LastViewedAt, uss.Threads[0].LastViewedAt)
|
||||
|
||||
timestamp := model.GetMillis()
|
||||
resp = th.Client.UpdateThreadReadForUser(th.BasicUser.Id, th.BasicTeam.Id, rrpost.Id, timestamp)
|
||||
_, resp = th.Client.UpdateThreadReadForUser(th.BasicUser.Id, th.BasicTeam.Id, rrpost.Id, timestamp)
|
||||
CheckNoError(t, resp)
|
||||
CheckOKStatus(t, resp)
|
||||
|
||||
|
||||
@@ -1020,8 +1020,8 @@ type AppIface interface {
|
||||
UpdateTeamMemberSchemeRoles(teamID string, userID string, isSchemeGuest bool, isSchemeUser bool, isSchemeAdmin bool) (*model.TeamMember, *model.AppError)
|
||||
UpdateTeamPrivacy(teamID string, teamType string, allowOpenInvite bool) *model.AppError
|
||||
UpdateTeamScheme(team *model.Team) (*model.Team, *model.AppError)
|
||||
UpdateThreadFollowForUser(userID, threadId string, state bool) *model.AppError
|
||||
UpdateThreadReadForUser(userID, teamID, threadId string, timestamp int64) *model.AppError
|
||||
UpdateThreadFollowForUser(userID, teamID, threadID string, state bool) *model.AppError
|
||||
UpdateThreadReadForUser(userID, teamID, threadID string, timestamp int64) (*model.ThreadResponse, *model.AppError)
|
||||
UpdateThreadsReadForUser(userID, teamID string) *model.AppError
|
||||
UpdateUser(user *model.User, sendNotifications bool) (*model.User, *model.AppError)
|
||||
UpdateUserActive(userID string, active bool) *model.AppError
|
||||
|
||||
@@ -2371,8 +2371,13 @@ func (a *App) MarkChannelAsUnreadFromPost(postID string, userID string) (*model.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if *a.Config().ServiceSettings.ThreadAutoFollow && post.RootId != "" {
|
||||
threadMembership, _ := a.Srv().Store.Thread().GetMembershipForUser(user.Id, post.RootId)
|
||||
if *a.Config().ServiceSettings.ThreadAutoFollow {
|
||||
threadId := post.RootId
|
||||
if post.RootId == "" {
|
||||
threadId = post.Id
|
||||
}
|
||||
|
||||
threadMembership, _ := a.Srv().Store.Thread().GetMembershipForUser(user.Id, threadId)
|
||||
if threadMembership != nil {
|
||||
channel, nErr := a.Srv().Store.Channel().Get(post.ChannelId, true)
|
||||
if nErr != nil {
|
||||
@@ -2386,6 +2391,20 @@ func (a *App) MarkChannelAsUnreadFromPost(postID string, userID string) (*model.
|
||||
if nErr != nil {
|
||||
return nil, model.NewAppError("MarkChannelAsUnreadFromPost", "app.channel.update_last_viewed_at_post.app_error", nil, nErr.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
thread, _ := a.Srv().Store.Thread().GetThreadForUser(userID, channel.TeamId, threadId, true)
|
||||
a.sanitizeProfiles(thread.Participants, false)
|
||||
thread.Post.SanitizeProps()
|
||||
|
||||
payload := thread.ToJson()
|
||||
sendEvent := *a.Config().ServiceSettings.CollapsedThreads == model.COLLAPSED_THREADS_DEFAULT_ON
|
||||
if preference, err := a.Srv().Store.Preference().Get(userID, model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, model.PREFERENCE_NAME_COLLAPSED_THREADS_ENABLED); err == nil {
|
||||
sendEvent = preference.Value == "on"
|
||||
}
|
||||
if sendEvent {
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_THREAD_UPDATED, channel.TeamId, "", userID, nil)
|
||||
message.Add("thread", payload)
|
||||
a.Publish(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
"context"
|
||||
"net/http"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
@@ -168,7 +167,7 @@ func (a *App) SendNotifications(post *model.Post, team *model.Team, channel *mod
|
||||
}
|
||||
}
|
||||
|
||||
mentionedUsersList := make([]string, 0, len(mentions.Mentions))
|
||||
mentionedUsersList := make(model.StringArray, 0, len(mentions.Mentions))
|
||||
updateMentionChans := []chan *model.AppError{}
|
||||
mentionAutofollowChans := []chan *model.AppError{}
|
||||
threadParticipants := map[string]bool{post.UserId: true}
|
||||
@@ -433,16 +432,19 @@ func (a *App) SendNotifications(post *model.Post, team *model.Team, channel *mod
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "cannot get thread %q", post.RootId)
|
||||
}
|
||||
payload := thread.ToJson()
|
||||
for _, uid := range thread.Participants {
|
||||
sendEvent := *a.Config().ServiceSettings.CollapsedThreads == model.COLLAPSED_THREADS_DEFAULT_ON
|
||||
// check if a participant has overridden collapsed threads settings
|
||||
if preference, err := a.Srv().Store.Preference().Get(uid, model.PREFERENCE_CATEGORY_COLLAPSED_THREADS_SETTINGS, model.PREFERENCE_NAME_COLLAPSED_THREADS_ENABLED); err == nil {
|
||||
sendEvent, _ = strconv.ParseBool(preference.Value)
|
||||
if preference, err := a.Srv().Store.Preference().Get(uid, model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, model.PREFERENCE_NAME_COLLAPSED_THREADS_ENABLED); err == nil {
|
||||
sendEvent = preference.Value == "on"
|
||||
}
|
||||
if sendEvent {
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_THREAD_UPDATED, "", "", uid, nil)
|
||||
message.Add("thread", payload)
|
||||
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_THREAD_UPDATED, team.Id, "", uid, nil)
|
||||
userThread, _ := a.Srv().Store.Thread().GetThreadForUser(uid, channel.TeamId, thread.PostId, true)
|
||||
a.sanitizeProfiles(userThread.Participants, false)
|
||||
userThread.Post.SanitizeProps()
|
||||
message.Add("thread", userThread.ToJson())
|
||||
a.Publish(message)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15643,7 +15643,7 @@ func (a *OpenTracingAppLayer) UpdateTeamScheme(team *model.Team) (*model.Team, *
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) UpdateThreadFollowForUser(userID string, threadId string, state bool) *model.AppError {
|
||||
func (a *OpenTracingAppLayer) UpdateThreadFollowForUser(userID string, teamID string, threadID string, state bool) *model.AppError {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.UpdateThreadFollowForUser")
|
||||
|
||||
@@ -15655,7 +15655,7 @@ func (a *OpenTracingAppLayer) UpdateThreadFollowForUser(userID string, threadId
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0 := a.app.UpdateThreadFollowForUser(userID, threadId, state)
|
||||
resultVar0 := a.app.UpdateThreadFollowForUser(userID, teamID, threadID, state)
|
||||
|
||||
if resultVar0 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar0))
|
||||
@@ -15665,7 +15665,7 @@ func (a *OpenTracingAppLayer) UpdateThreadFollowForUser(userID string, threadId
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) UpdateThreadReadForUser(userID string, teamID string, threadId string, timestamp int64) *model.AppError {
|
||||
func (a *OpenTracingAppLayer) UpdateThreadReadForUser(userID string, teamID string, threadID string, timestamp int64) (*model.ThreadResponse, *model.AppError) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.UpdateThreadReadForUser")
|
||||
|
||||
@@ -15677,14 +15677,14 @@ func (a *OpenTracingAppLayer) UpdateThreadReadForUser(userID string, teamID stri
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0 := a.app.UpdateThreadReadForUser(userID, teamID, threadId, timestamp)
|
||||
resultVar0, resultVar1 := a.app.UpdateThreadReadForUser(userID, teamID, threadID, timestamp)
|
||||
|
||||
if resultVar0 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar0))
|
||||
if resultVar1 != nil {
|
||||
span.LogFields(spanlog.Error(resultVar1))
|
||||
ext.Error.Set(span, true)
|
||||
}
|
||||
|
||||
return resultVar0
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) UpdateThreadsReadForUser(userID string, teamID string) *model.AppError {
|
||||
|
||||
@@ -1390,8 +1390,10 @@ func (a *App) countThreadMentions(user *model.User, post *model.Post, teamID str
|
||||
}
|
||||
|
||||
mentions := getExplicitMentions(post, keywords, groups)
|
||||
if _, ok := mentions.Mentions[user.Id]; ok {
|
||||
count += 1
|
||||
if post.UpdateAt >= timestamp {
|
||||
if _, ok := mentions.Mentions[user.Id]; ok {
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
|
||||
for _, p := range posts {
|
||||
|
||||
44
app/user.go
44
app/user.go
@@ -2397,54 +2397,60 @@ func (a *App) UpdateThreadsReadForUser(userID, teamID string) *model.AppError {
|
||||
if nErr != nil {
|
||||
return model.NewAppError("UpdateThreadsReadForUser", "app.user.update_threads_read_for_user.app_error", nil, nErr.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_THREAD_READ_CHANGED, "", "", userID, nil)
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_THREAD_READ_CHANGED, teamID, "", userID, nil)
|
||||
a.Publish(message)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) UpdateThreadFollowForUser(userID, threadId string, state bool) *model.AppError {
|
||||
err := a.Srv().Store.Thread().CreateMembershipIfNeeded(userID, threadId, state, false, true)
|
||||
func (a *App) UpdateThreadFollowForUser(userID, teamID, threadID string, state bool) *model.AppError {
|
||||
err := a.Srv().Store.Thread().CreateMembershipIfNeeded(userID, threadID, state, false, true)
|
||||
if err != nil {
|
||||
return model.NewAppError("UpdateThreadFollowForUser", "app.user.update_thread_follow_for_user.app_error", nil, err.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_THREAD_FOLLOW_CHANGED, "", "", userID, nil)
|
||||
message.Add("thread_id", threadId)
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_THREAD_FOLLOW_CHANGED, teamID, "", userID, nil)
|
||||
message.Add("thread_id", threadID)
|
||||
message.Add("state", state)
|
||||
a.Publish(message)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) UpdateThreadReadForUser(userID, teamID, threadId string, timestamp int64) *model.AppError {
|
||||
func (a *App) UpdateThreadReadForUser(userID, teamID, threadID string, timestamp int64) (*model.ThreadResponse, *model.AppError) {
|
||||
user, err := a.GetUser(userID)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
membership, nErr := a.Srv().Store.Thread().GetMembershipForUser(userID, threadId)
|
||||
membership, nErr := a.Srv().Store.Thread().GetMembershipForUser(userID, threadID)
|
||||
if nErr != nil {
|
||||
return model.NewAppError("UpdateThreadsReadForUser", "app.user.update_threads_read_for_user.app_error", nil, nErr.Error(), http.StatusInternalServerError)
|
||||
return nil, model.NewAppError("UpdateThreadsReadForUser", "app.user.update_threads_read_for_user.app_error", nil, nErr.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
post, err := a.GetSinglePost(threadId)
|
||||
post, err := a.GetSinglePost(threadID)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
membership.UnreadMentions, err = a.countThreadMentions(user, post, teamID, timestamp)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
membership.Following = true
|
||||
_, nErr = a.Srv().Store.Thread().UpdateMembership(membership)
|
||||
if nErr != nil {
|
||||
return model.NewAppError("UpdateThreadsReadForUser", "app.user.update_threads_read_for_user.app_error", nil, nErr.Error(), http.StatusInternalServerError)
|
||||
return nil, model.NewAppError("UpdateThreadsReadForUser", "app.user.update_threads_read_for_user.app_error", nil, nErr.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
nErr = a.Srv().Store.Thread().MarkAsRead(userID, threadId, timestamp)
|
||||
nErr = a.Srv().Store.Thread().MarkAsRead(userID, threadID, timestamp)
|
||||
if nErr != nil {
|
||||
return model.NewAppError("UpdateThreadReadForUser", "app.user.update_thread_read_for_user.app_error", nil, nErr.Error(), http.StatusInternalServerError)
|
||||
return nil, model.NewAppError("UpdateThreadReadForUser", "app.user.update_thread_read_for_user.app_error", nil, nErr.Error(), http.StatusInternalServerError)
|
||||
}
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_THREAD_READ_CHANGED, "", "", userID, nil)
|
||||
message.Add("thread_id", threadId)
|
||||
thread, err := a.GetThreadForUser(userID, teamID, threadID, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
message := model.NewWebSocketEvent(model.WEBSOCKET_EVENT_THREAD_READ_CHANGED, teamID, "", userID, nil)
|
||||
message.Add("thread_id", threadID)
|
||||
message.Add("timestamp", timestamp)
|
||||
message.Add("unread_mentions", membership.UnreadMentions)
|
||||
message.Add("unread_replies", thread.UnreadReplies)
|
||||
message.Add("channel_id", post.ChannelId)
|
||||
a.Publish(message)
|
||||
return nil
|
||||
return thread, nil
|
||||
}
|
||||
|
||||
@@ -5966,14 +5966,16 @@ func (c *Client4) UpdateThreadsReadForUser(userId, teamId string) *Response {
|
||||
return BuildResponse(r)
|
||||
}
|
||||
|
||||
func (c *Client4) UpdateThreadReadForUser(userId, teamId, threadId string, timestamp int64) *Response {
|
||||
func (c *Client4) UpdateThreadReadForUser(userId, teamId, threadId string, timestamp int64) (*ThreadResponse, *Response) {
|
||||
r, appErr := c.DoApiPut(fmt.Sprintf("%s/read/%d", c.GetUserThreadRoute(userId, teamId, threadId), timestamp), "")
|
||||
if appErr != nil {
|
||||
return BuildErrorResponse(r, appErr)
|
||||
return nil, BuildErrorResponse(r, appErr)
|
||||
}
|
||||
defer closeBody(r)
|
||||
var thread ThreadResponse
|
||||
json.NewDecoder(r.Body).Decode(&thread)
|
||||
|
||||
return BuildResponse(r)
|
||||
return &thread, BuildResponse(r)
|
||||
}
|
||||
|
||||
func (c *Client4) UpdateThreadFollowForUser(userId, teamId, threadId string, state bool) *Response {
|
||||
|
||||
@@ -13,17 +13,16 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW = "direct_channel_show"
|
||||
PREFERENCE_CATEGORY_GROUP_CHANNEL_SHOW = "group_channel_show"
|
||||
PREFERENCE_CATEGORY_TUTORIAL_STEPS = "tutorial_step"
|
||||
PREFERENCE_CATEGORY_ADVANCED_SETTINGS = "advanced_settings"
|
||||
PREFERENCE_CATEGORY_FLAGGED_POST = "flagged_post"
|
||||
PREFERENCE_CATEGORY_FAVORITE_CHANNEL = "favorite_channel"
|
||||
PREFERENCE_CATEGORY_SIDEBAR_SETTINGS = "sidebar_settings"
|
||||
PREFERENCE_CATEGORY_COLLAPSED_THREADS_SETTINGS = "collapsed_threads_settings"
|
||||
PREFERENCE_CATEGORY_DIRECT_CHANNEL_SHOW = "direct_channel_show"
|
||||
PREFERENCE_CATEGORY_GROUP_CHANNEL_SHOW = "group_channel_show"
|
||||
PREFERENCE_CATEGORY_TUTORIAL_STEPS = "tutorial_step"
|
||||
PREFERENCE_CATEGORY_ADVANCED_SETTINGS = "advanced_settings"
|
||||
PREFERENCE_CATEGORY_FLAGGED_POST = "flagged_post"
|
||||
PREFERENCE_CATEGORY_FAVORITE_CHANNEL = "favorite_channel"
|
||||
PREFERENCE_CATEGORY_SIDEBAR_SETTINGS = "sidebar_settings"
|
||||
|
||||
PREFERENCE_CATEGORY_DISPLAY_SETTINGS = "display_settings"
|
||||
PREFERENCE_NAME_COLLAPSED_THREADS_ENABLED = "collapsed_threads_enabled"
|
||||
PREFERENCE_NAME_COLLAPSED_THREADS_ENABLED = "collapsed_reply_threads"
|
||||
PREFERENCE_NAME_CHANNEL_DISPLAY_MODE = "channel_display_mode"
|
||||
PREFERENCE_NAME_COLLAPSE_SETTING = "collapse_previews"
|
||||
PREFERENCE_NAME_MESSAGE_DISPLAY = "message_display"
|
||||
|
||||
@@ -61,6 +61,12 @@ func (o *ThreadResponse) ToJson() string {
|
||||
return string(b)
|
||||
}
|
||||
|
||||
func ThreadResponseFromJson(s string) (*ThreadResponse, error) {
|
||||
var t ThreadResponse
|
||||
err := json.Unmarshal([]byte(s), &t)
|
||||
return &t, err
|
||||
}
|
||||
|
||||
func (o *Threads) ToJson() string {
|
||||
b, _ := json.Marshal(o)
|
||||
return string(b)
|
||||
|
||||
Reference in New Issue
Block a user