Skip to content

Commit f7f0251

Browse files
rafaelmf3Rafael Marinho
andauthored
[CHA-647]: support shared locations (#326)
* feat(cha-769): support shared locations * minor fixes * refactor * fix unit test * fix unit tests * fix unit test * feat(cha-769): support shared location * clean up * clean up --------- Co-authored-by: Rafael Marinho <[email protected]>
1 parent 3b3a8e3 commit f7f0251

File tree

5 files changed

+201
-0
lines changed

5 files changed

+201
-0
lines changed

app.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ type AppSettings struct {
5353
ChannelHideMembersOnly *bool `json:"channel_hide_members_only,omitempty"`
5454
AsyncModerationConfig *AsyncModerationConfiguration `json:"async_moderation_config,omitempty"`
5555
EventHooks []EventHook `json:"event_hooks,omitempty"`
56+
SharedLocationsEnabled *bool `json:"shared_locations_enabled,omitempty"`
5657
}
5758

5859
func (a *AppSettings) SetDisableAuth(b bool) *AppSettings {
@@ -100,6 +101,11 @@ func (a *AppSettings) SetEventHooks(eventHooks []EventHook) *AppSettings {
100101
return a
101102
}
102103

104+
func (a *AppSettings) SetSharedLocationsEnabled(b bool) *AppSettings {
105+
a.SharedLocationsEnabled = &b
106+
return a
107+
}
108+
103109
func NewAppSettings() *AppSettings {
104110
return &AppSettings{}
105111
}

message.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ type Message struct {
6565
DeletedAt *time.Time `json:"deleted_at,omitempty"`
6666

6767
ExtraData map[string]interface{} `json:"-"`
68+
69+
SharedLocation *SharedLocation `json:"shared_location,omitempty"`
6870
}
6971

7072
type messageForJSON Message
@@ -107,6 +109,7 @@ func (m *Message) toRequest() messageRequest {
107109
Silent: m.Silent,
108110
QuotedMessageID: m.QuotedMessageID,
109111
RestrictedVisibility: m.RestrictedVisibility,
112+
SharedLocation: m.SharedLocation,
110113
}
111114

112115
if len(m.MentionedUsers) > 0 {
@@ -145,6 +148,7 @@ type messageRequestMessage struct {
145148
HTML string `json:"html,omitempty"`
146149
Pinned bool `json:"pinned,omitempty"`
147150
RestrictedVisibility []string `json:"restricted_visibility"`
151+
SharedLocation *SharedLocation `json:"shared_location,omitempty"`
148152
ExtraData map[string]interface{} `json:"-"`
149153
}
150154

shared_locations.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package stream_chat
2+
3+
import (
4+
"context"
5+
"errors"
6+
"net/http"
7+
"net/url"
8+
"path"
9+
"time"
10+
)
11+
12+
type SharedLocation struct {
13+
MessageID string `json:"message_id"`
14+
ChannelCID string `json:"channel_cid"`
15+
UserID string `json:"user_id"`
16+
17+
Latitude *float64 `json:"latitude,omitempty"`
18+
Longitude *float64 `json:"longitude,omitempty"`
19+
CreatedByDeviceID string `json:"created_by_device_id"`
20+
EndAt *time.Time `json:"end_at,omitempty"`
21+
22+
CreatedAt time.Time `json:"created_at"`
23+
UpdatedAt time.Time `json:"updated_at"`
24+
}
25+
26+
type ActiveLiveLocationsResponse struct {
27+
ActiveLiveLocations []*SharedLocation `json:"active_live_locations"`
28+
Response
29+
}
30+
31+
type SharedLocationResponse struct {
32+
SharedLocation
33+
34+
Message *MessageResponse `json:"message,omitempty"`
35+
Channel *Channel `json:"channel,omitempty"`
36+
}
37+
38+
// GetUserActiveLocations returns all active live locations for a user
39+
func (c *Client) GetUserActiveLocations(ctx context.Context, userID string) (*ActiveLiveLocationsResponse, error) {
40+
path := path.Join("users", "live_locations")
41+
var resp ActiveLiveLocationsResponse
42+
if userID == "" {
43+
return nil, errors.New("user ID is empty")
44+
}
45+
46+
if userID == "" {
47+
return nil, errors.New("user ID is empty")
48+
}
49+
50+
params := url.Values{}
51+
params.Set("user_id", userID)
52+
53+
err := c.makeRequest(ctx, http.MethodGet, path, params, nil, &resp)
54+
return &resp, err
55+
}
56+
57+
// UpdateUserActiveLocation updates a location
58+
func (c *Client) UpdateUserActiveLocation(ctx context.Context, userID string, location *SharedLocation) (*SharedLocationResponse, error) {
59+
path := path.Join("users", "live_locations")
60+
var resp SharedLocationResponse
61+
62+
if userID == "" {
63+
return nil, errors.New("user ID is empty")
64+
}
65+
66+
params := url.Values{}
67+
params.Set("user_id", userID)
68+
69+
err := c.makeRequest(ctx, http.MethodPut, path, params, location, &resp)
70+
return &resp, err
71+
}

shared_locations_test.go

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package stream_chat
2+
3+
import (
4+
"context"
5+
"testing"
6+
"time"
7+
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
func createTestChannelWithSharedLocations(t *testing.T, c *Client) (*Channel, *User) {
13+
t.Helper()
14+
15+
ctx := context.Background()
16+
userID := "test-user-" + randomString(10)
17+
channelID := "test-channel-" + randomString(10)
18+
19+
user := &User{ID: userID}
20+
resp, err := c.UpsertUser(ctx, user)
21+
require.NoError(t, err)
22+
user = resp.User
23+
24+
channelResp, err := c.CreateChannelWithMembers(ctx, "messaging", channelID, userID)
25+
require.NoError(t, err)
26+
27+
_, err = channelResp.Channel.PartialUpdate(ctx, PartialUpdate{
28+
Set: map[string]interface{}{
29+
"config_overrides": map[string]interface{}{
30+
"shared_locations": true,
31+
},
32+
},
33+
})
34+
require.NoError(t, err)
35+
36+
t.Cleanup(func() {
37+
_, _ = c.DeleteUsers(ctx, []string{userID}, DeleteUserOptions{
38+
User: HardDelete,
39+
Messages: HardDelete,
40+
Conversations: HardDelete,
41+
})
42+
_, _ = c.DeleteChannels(ctx, []string{channelResp.Channel.CID}, true)
43+
})
44+
45+
return channelResp.Channel, user
46+
}
47+
48+
func TestClient_LiveLocation(t *testing.T) {
49+
c := initClient(t)
50+
ctx := context.Background()
51+
52+
settings := NewAppSettings().SetSharedLocationsEnabled(true)
53+
54+
_, err := c.UpdateAppSettings(ctx, settings)
55+
require.NoError(t, err)
56+
57+
// Create a user
58+
channel, user := createTestChannelWithSharedLocations(t, c)
59+
60+
longitude := -122.4194
61+
latitude := 38.999
62+
63+
// Create a shared location
64+
location := &SharedLocation{
65+
Longitude: &longitude,
66+
Latitude: &latitude,
67+
EndAt: timePtr(time.Now().Add(1 * time.Hour)),
68+
CreatedByDeviceID: "test-device",
69+
}
70+
71+
messageResp, err := channel.SendMessage(ctx, &Message{
72+
SharedLocation: location,
73+
Text: "Test message for shared location",
74+
}, user.ID)
75+
require.NoError(t, err)
76+
message := messageResp.Message
77+
78+
longitude = -122.4194
79+
latitude = 38.999
80+
81+
newLocation := &SharedLocation{
82+
MessageID: message.ID,
83+
Longitude: &longitude,
84+
Latitude: &latitude,
85+
EndAt: timePtr(time.Now().Add(10 * time.Hour)),
86+
CreatedByDeviceID: "test-device",
87+
}
88+
89+
// Update the location
90+
updateResp1, err := c.UpdateUserActiveLocation(ctx, user.ID, newLocation)
91+
require.NoError(t, err, "UpdateUserActiveLocation should not return an error")
92+
require.NotNil(t, updateResp1)
93+
assert.Equal(t, newLocation.Latitude, updateResp1.Latitude)
94+
assert.Equal(t, newLocation.Longitude, updateResp1.Longitude)
95+
96+
// Get active live locations
97+
getResp, err := c.GetUserActiveLocations(ctx, user.ID)
98+
require.NoError(t, err, "GetUserActiveLocations should not return an error")
99+
require.NotNil(t, getResp)
100+
require.NotEmpty(t, getResp.ActiveLiveLocations, "Should have active live locations")
101+
102+
// Verify the location data
103+
found := false
104+
for _, loc := range getResp.ActiveLiveLocations {
105+
if loc.MessageID == messageResp.Message.ID {
106+
found = true
107+
assert.Equal(t, messageResp.Message.SharedLocation.Latitude, loc.Latitude)
108+
assert.Equal(t, messageResp.Message.SharedLocation.Longitude, loc.Longitude)
109+
break
110+
}
111+
}
112+
assert.True(t, found, "Should find the updated location")
113+
}

user_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"log"
66
"testing"
7+
"time"
78

89
"github.com/stretchr/testify/assert"
910
"github.com/stretchr/testify/require"
@@ -452,6 +453,12 @@ func TestClient_RestoreUsers(t *testing.T) {
452453
}
453454
})
454455
}
456+
457+
// Helper function to create a time pointer
458+
func timePtr(t time.Time) *time.Time {
459+
return &t
460+
}
461+
455462
func ExampleClient_UpsertUser() {
456463
client, _ := NewClient("XXXX", "XXXX")
457464
ctx := context.Background()

0 commit comments

Comments
 (0)