diff --git a/doc/api.oa3 b/doc/api.oa3 index de53aa79..21cca956 100644 --- a/doc/api.oa3 +++ b/doc/api.oa3 @@ -2118,7 +2118,7 @@ paths: tags: - content description: Get details of channel. - operationId: get-channel + operationId: get-channel-detail security: - bearerAuth: [] parameters: @@ -2134,7 +2134,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Channel' + $ref: '#/components/schemas/ChannelDetail' '401': description: invalid password '404': @@ -2144,7 +2144,7 @@ paths: '500': description: internal server error -/content/channels/{channelId}: + /content/channels/{channelId}: delete: tags: - content @@ -2472,43 +2472,6 @@ paths: schema: $ref: '#/components/schemas/Subject' - /content/channels/{channelId}/topics/{topicId}/detail: - get: - tags: - - content - description: Get full object of topic. - operationId: get-channel-topic - security: - - bearerAuth: [] - parameters: - - name: channelId - in: path - description: specified channel id - required: true - schema: - type: string - - name: topicId - in: path - description: specified topic id - required: true - schema: - type: string - responses: - '200': - description: success - content: - application/json: - schema: - $ref: '#/components/schemas/Topic' - '401': - description: invalid password - '404': - description: channel not found - '410': - description: account disabled - '500': - description: internal server error - /content/channels/{channelId}/topics/{topicId}: delete: tags: @@ -3374,7 +3337,8 @@ components: node: type: string revision: - type: int64 + type: integer + format: int64 CardDetail: type: object @@ -3500,7 +3464,7 @@ components: $ref: '#/components/schemas/ChannelContacts' members: type: array - item: + items: type: string ChannelContacts: @@ -3860,7 +3824,3 @@ components: - - - - diff --git a/net/server/internal/api_getChannel.go b/net/server/internal/api_getChannel.go deleted file mode 100644 index 56d37836..00000000 --- a/net/server/internal/api_getChannel.go +++ /dev/null @@ -1,62 +0,0 @@ -package databag - -import ( - "errors" - "net/http" - "gorm.io/gorm" - "github.com/gorilla/mux" - "databag/internal/store" -) - -func GetChannel(w http.ResponseWriter, r *http.Request) { - - // scan parameters - params := mux.Vars(r) - channelId := params["channelId"] - - var guid string - var act *store.Account - tokenType := ParamTokenType(r) - if tokenType == APP_TOKENAGENT { - account, code, err := ParamAgentToken(r, false); - if err != nil { - ErrResponse(w, code, err) - return - } - act = account - } else if tokenType == APP_TOKENCONTACT { - card, code, err := ParamContactToken(r, true) - if err != nil { - ErrResponse(w, code, err) - return - } - act = &card.Account - guid = card.Guid - } else { - ErrResponse(w, http.StatusBadRequest, errors.New("unknown token type")) - return - } - - // load channel - var slot store.ChannelSlot - if err := store.DB.Preload("Channel.Cards.CardSlot").Preload("Channel.Groups.Cards").Preload("Channel.Groups.GroupSlot").Where("account_id = ? AND channel_slot_id = ?", act.ID, channelId).First(&slot).Error; err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - ErrResponse(w, http.StatusNotFound, err) - } else { - ErrResponse(w, http.StatusInternalServerError, err) - } - return - } - - // return model data - if guid != "" { - if isChannelShared(guid, slot.Channel) { - WriteResponse(w, getChannelModel(&slot, true, false)) - } else { - WriteResponse(w, getChannelModel(&slot, false, false)) - } - } else { - WriteResponse(w, getChannelModel(&slot, true, true)) - } -} - diff --git a/net/server/internal/api_getChannels.go b/net/server/internal/api_getChannels.go index d38c821e..b5869d98 100644 --- a/net/server/internal/api_getChannels.go +++ b/net/server/internal/api_getChannels.go @@ -5,6 +5,7 @@ import ( "errors" "strconv" "net/http" + "gorm.io/gorm" "encoding/json" "databag/internal/store" ) @@ -101,7 +102,9 @@ func GetChannels(w http.ResponseWriter, r *http.Request) { account := &card.Account var slots []store.ChannelSlot if channelRevisionSet { - if err := store.DB.Preload("Channel.Cards").Preload("Channel.Groups.Cards").Where("account_id = ? AND revision > ?", account.ID, channelRevision).Find(&slots).Error; err != nil { + if err := store.DB.Preload("Channel.Topics", func(db *gorm.DB) *gorm.DB { + return store.DB.Order("topics.id DESC").Limit(1) + }).Preload("Channel.Cards").Preload("Channel.Groups.Cards").Where("account_id = ? AND revision > ?", account.ID, channelRevision).Find(&slots).Error; err != nil { ErrResponse(w, http.StatusInternalServerError, err) return } diff --git a/net/server/internal/modelUtil.go b/net/server/internal/modelUtil.go index c4c20e55..2075824b 100644 --- a/net/server/internal/modelUtil.go +++ b/net/server/internal/modelUtil.go @@ -165,13 +165,10 @@ func getChannelRevisionModel(slot *store.ChannelSlot, showData bool) *Channel { } } -func getChannelModel(slot *store.ChannelSlot, showData bool, showList bool) *Channel { +func getChannelDetailModel(slot *store.ChannelSlot, showList bool) *ChannelDetail { - if !showData || slot.Channel == nil { - return &Channel{ - Id: slot.ChannelSlotId, - Revision: slot.Revision, - } + if slot.Channel == nil { + return nil } var contacts *ChannelContacts @@ -192,20 +189,32 @@ func getChannelModel(slot *store.ChannelSlot, showData bool, showList bool) *Cha members = append(members, card.Guid) } + return &ChannelDetail{ + DataType: slot.Channel.DataType, + Data: slot.Channel.Data, + Created: slot.Channel.Created, + Updated: slot.Channel.Updated, + Contacts: contacts, + Members: members, + } +} + +func getChannelModel(slot *store.ChannelSlot, showData bool, showList bool) *Channel { + + if !showData || slot.Channel == nil { + return &Channel{ + Id: slot.ChannelSlotId, + Revision: slot.Revision, + } + } + return &Channel{ Id: slot.ChannelSlotId, Revision: slot.Revision, Data: &ChannelData { DetailRevision: slot.Channel.DetailRevision, TopicRevision: slot.Channel.TopicRevision, - ChannelDetail: &ChannelDetail{ - DataType: slot.Channel.DataType, - Data: slot.Channel.Data, - Created: slot.Channel.Created, - Updated: slot.Channel.Updated, - Contacts: contacts, - Members: members, - }, + ChannelDetail: getChannelDetailModel(slot, showList), }, } } diff --git a/net/server/internal/routers.go b/net/server/internal/routers.go index a5a5aaf8..2bb54e07 100644 --- a/net/server/internal/routers.go +++ b/net/server/internal/routers.go @@ -539,10 +539,10 @@ var routes = Routes{ }, Route{ - "GetChannel", + "GetChannelDetail", strings.ToUpper("Get"), "/content/channels/{channelId}/detail", - GetChannel, + GetChannelDetail, }, Route{ diff --git a/net/server/internal/testApp.go b/net/server/internal/testApp.go index 5308aacc..f5fd9dc0 100644 --- a/net/server/internal/testApp.go +++ b/net/server/internal/testApp.go @@ -215,16 +215,16 @@ func (c *TestContactData) UpdateContactChannel(storeChannel *TestChannel, channe token := c.card.Data.CardProfile.Guid + "." + c.card.Data.CardDetail.Token params := &TestApiParams{ query: "/channel/{channelId}", path: map[string]string{ "channelId": channel.Id }, tokenType: APP_TOKENCONTACT, token: token } - channel := Channel{} - response := &TestApiResponse{ data: &channel } - if err = TestApiRequest(GetChannel, params, response); err != nil { + detail := &ChannelDetail{} + response := &TestApiResponse{ data: &detail } + if err = TestApiRequest(GetChannelDetail, params, response); err != nil { return } if channel.Data == nil { err = errors.New("channel removed during update") return } - storeChannel.channel.Data.ChannelDetail = channel.Data.ChannelDetail + storeChannel.channel.Data.ChannelDetail = detail storeChannel.channel.Data.DetailRevision = channel.Data.DetailRevision } } @@ -507,16 +507,16 @@ func (a *TestApp) UpdateChannel(storeChannel *TestChannel, channel *Channel) (er } else if storeChannel.channel.Data.DetailRevision != channel.Data.DetailRevision { params := &TestApiParams{ query: "/channel/{channelId}", path: map[string]string{ "channelId": channel.Id }, tokenType: APP_TOKENAGENT, token: a.token } - channel := Channel{} - response := &TestApiResponse{ data: &channel } - if err = TestApiRequest(GetChannel, params, response); err != nil { + detail := &ChannelDetail{} + response := &TestApiResponse{ data: &detail } + if err = TestApiRequest(GetChannelDetail, params, response); err != nil { return } if channel.Data == nil { err = errors.New("channel removed during update") return } - storeChannel.channel.Data.ChannelDetail = channel.Data.ChannelDetail + storeChannel.channel.Data.ChannelDetail = detail storeChannel.channel.Data.DetailRevision = channel.Data.DetailRevision } } diff --git a/net/server/internal/ucChannelShare_test.go b/net/server/internal/ucChannelShare_test.go index 3151e9e8..cc28fa6b 100644 --- a/net/server/internal/ucChannelShare_test.go +++ b/net/server/internal/ucChannelShare_test.go @@ -9,6 +9,7 @@ import ( func TestChannelShare(t *testing.T) { var subject *Subject var channel *Channel + var detail *ChannelDetail var channels *[]Channel var cards *[]Card aRevision := make(map[string][]string) @@ -79,13 +80,13 @@ func TestChannelShare(t *testing.T) { assert.Nil(t, (*channels)[0].Data) // get discovered channel - channel = &Channel{} - assert.NoError(t, ApiTestMsg(GetChannel, "GET", "/content/channels/{channelId}", - ¶ms, nil, APP_TOKENCONTACT, set.B.A.Token, channel, nil)) - assert.Equal(t, "channeldatatype", channel.Data.ChannelDetail.DataType) - assert.Equal(t, 1, len(channel.Data.ChannelDetail.Members)) - assert.Equal(t, set.B.Guid, channel.Data.ChannelDetail.Members[0]) - assert.Nil(t, channel.Data.ChannelDetail.Contacts) + detail = &ChannelDetail{} + assert.NoError(t, ApiTestMsg(GetChannelDetail, "GET", "/content/channels/{channelId}/detail", + ¶ms, nil, APP_TOKENCONTACT, set.B.A.Token, detail, nil)) + assert.Equal(t, "channeldatatype", detail.DataType) + assert.Equal(t, 1, len(detail.Members)) + assert.Equal(t, set.B.Guid, detail.Members[0]) + assert.Nil(t, detail.Contacts) // get revision aRev = GetTestRevision(set.A.Revisions) @@ -150,12 +151,12 @@ func TestChannelShare(t *testing.T) { assert.Equal(t, 1, len(*channels)) // get discovered channel - channel = &Channel{} - assert.NoError(t, ApiTestMsg(GetChannel, "GET", "/content/channels/{channelId}", - ¶ms, nil, APP_TOKENCONTACT, set.C.A.Token, channel, nil)) - assert.Equal(t, "channeldatatype", channel.Data.ChannelDetail.DataType) - assert.Equal(t, 2, len(channel.Data.ChannelDetail.Members)) - assert.Nil(t, channel.Data.ChannelDetail.Contacts) + detail = &ChannelDetail{} + assert.NoError(t, ApiTestMsg(GetChannelDetail, "GET", "/content/channels/{channelId}/detail", + ¶ms, nil, APP_TOKENCONTACT, set.C.A.Token, detail, nil)) + assert.Equal(t, "channeldatatype", detail.DataType) + assert.Equal(t, 2, len(detail.Members)) + assert.Nil(t, detail.Contacts) // reset notification GetTestRevision(set.B.Revisions) diff --git a/net/server/internal/ucTopicShare_test.go b/net/server/internal/ucTopicShare_test.go index c60c105b..ca6b2560 100644 --- a/net/server/internal/ucTopicShare_test.go +++ b/net/server/internal/ucTopicShare_test.go @@ -12,6 +12,7 @@ import ( func TestTopicShare(t *testing.T) { var topic *Topic var channel *Channel + var detail *ChannelDetail var subject *Subject params := make(map[string]string) header := make(map[string][]string) @@ -43,17 +44,17 @@ func TestTopicShare(t *testing.T) { ¶ms, nil, APP_TOKENAGENT, set.A.Token, nil, nil)) // view channel - channel = &Channel{} - assert.NoError(t, ApiTestMsg(GetChannel, "GET", "/content/channels/{channelId}", - ¶ms, nil, APP_TOKENAGENT, set.A.Token, channel, nil)) + detail = &ChannelDetail{} + assert.NoError(t, ApiTestMsg(GetChannelDetail, "GET", "/content/channels/{channelId}/detail", + ¶ms, nil, APP_TOKENAGENT, set.A.Token, detail, nil)) + assert.NotNil(t, detail); + detail = &ChannelDetail{} + assert.NoError(t, ApiTestMsg(GetChannelDetail, "GET", "/content/channels/{channelId}/detail", + ¶ms, nil, APP_TOKENCONTACT, set.B.A.Token, detail, nil)) assert.NotNil(t, channel.Data.ChannelDetail); - channel = &Channel{} - assert.NoError(t, ApiTestMsg(GetChannel, "GET", "/content/channels/{channelId}", - ¶ms, nil, APP_TOKENCONTACT, set.B.A.Token, channel, nil)) - assert.NotNil(t, channel.Data.ChannelDetail); - channel = &Channel{} - assert.NoError(t, ApiTestMsg(GetChannel, "GET", "/content/channels/{channelId}", - ¶ms, nil, APP_TOKENCONTACT, set.B.A.Token, channel, nil)) + detail = &ChannelDetail{} + assert.NoError(t, ApiTestMsg(GetChannelDetail, "GET", "/content/channels/{channelId}/detail", + ¶ms, nil, APP_TOKENCONTACT, set.B.A.Token, detail, nil)) assert.NotNil(t, channel.Data.ChannelDetail); params["field"] = "nested.image" data, header, err = ApiTestData(GetChannelSubjectField, "GET", "/content/channels/{channelId}/subject/{field}", diff --git a/net/web/src/User/Conversation/TopicItem/VideoAsset/VideoAsset.jsx b/net/web/src/User/Conversation/TopicItem/VideoAsset/VideoAsset.jsx index 8b218b94..ecffdd4c 100644 --- a/net/web/src/User/Conversation/TopicItem/VideoAsset/VideoAsset.jsx +++ b/net/web/src/User/Conversation/TopicItem/VideoAsset/VideoAsset.jsx @@ -18,13 +18,13 @@ export function VideoAsset({ thumbUrl, lqUrl, hdUrl }) { }, [thumbUrl, hdUrl, lqUrl]); const onFullScreen = () => { - updateState({ fullscreen: true, modalUrl: hdUrl, playing: false, url: null }); + updateState({ inline: false, popout: true, popoutUrl: hdUrl, playing: false, inlineUrl: null }); } const CenterButton = () => { - if (!state.loaded) { + if (!state.inline) { return ( -
updateState({ loaded: true, url: lqUrl, controls: false, paused: true, playing: false })}> +
updateState({ inline: true, inlineUrl: lqUrl, playing: false })}>
) @@ -46,15 +46,12 @@ export function VideoAsset({ thumbUrl, lqUrl, hdUrl }) { } const Controls = () => { - if (state.controls) { - return <>; - } return (
-
onFullScreen()}> +
onFullScreen()}>
@@ -72,12 +69,12 @@ export function VideoAsset({ thumbUrl, lqUrl, hdUrl }) { }}
- +
- { updateState({ fullscreen: false, modalUrl: null })}}> - + { updateState({ popout: false })}}> + ) diff --git a/net/web/src/User/Conversation/TopicItem/VideoAsset/VideoAsset.styled.js b/net/web/src/User/Conversation/TopicItem/VideoAsset/VideoAsset.styled.js index a3459b28..a04b4f62 100644 --- a/net/web/src/User/Conversation/TopicItem/VideoAsset/VideoAsset.styled.js +++ b/net/web/src/User/Conversation/TopicItem/VideoAsset/VideoAsset.styled.js @@ -16,7 +16,7 @@ export const VideoAssetWrapper = styled.div` visibility: visible; } - .player:hover .fullscreen { + .player:hover .expand { visibility: visible; } @@ -38,7 +38,7 @@ export const VideoAssetWrapper = styled.div` opacity: 0.5; } - .fullscreen { + .expand { padding-right: 2px; visibility: hidden; position: absolute; diff --git a/net/web/src/api/getChannel.js b/net/web/src/api/getChannel.js deleted file mode 100644 index 27d58d1a..00000000 --- a/net/web/src/api/getChannel.js +++ /dev/null @@ -1,8 +0,0 @@ -import { checkResponse, fetchWithTimeout } from './fetchUtil'; - -export async function getChannel(token, channelId) { - let channel = await fetchWithTimeout(`/content/channels/${channelId}/detail?agent=${token}`, { method: 'GET' }); - checkResponse(channel) - return await channel.json() -} - diff --git a/net/web/src/api/getContactChannel.js b/net/web/src/api/getContactChannel.js deleted file mode 100644 index c6a447b5..00000000 --- a/net/web/src/api/getContactChannel.js +++ /dev/null @@ -1,8 +0,0 @@ -import { checkResponse, fetchWithTimeout } from './fetchUtil'; - -export async function getContactChannel(token, channelId) { - let channel = await fetchWithTimeout(`/content/channels/${channelId}/detail?contact=${token}`, { method: 'GET' }); - checkResponse(channel) - return await channel.json() -} - diff --git a/net/web/src/context/useCardContext.hook.js b/net/web/src/context/useCardContext.hook.js index f7ec578d..a02aa2ce 100644 --- a/net/web/src/context/useCardContext.hook.js +++ b/net/web/src/context/useCardContext.hook.js @@ -1,6 +1,6 @@ import { useEffect, useState, useRef } from 'react'; import { getContactChannels } from 'api/getContactChannels'; -import { getContactChannel } from 'api/getContactChannel'; +import { getContactChannelDetail } from 'api/getContactChannelDetail'; import { getContactProfile } from 'api/getContactProfile'; import { setCardProfile } from 'api/setCardProfile'; import { getCards } from 'api/getCards'; @@ -119,9 +119,9 @@ export function useCardContext() { cur.data.detailRevision = channel.data.detailRevision; } else { - let slot = await getContactChannel(guid + "." + token, channel.id); - cur.data.channelDetail = slot.data.channelDetail; - cur.data.detailRevision = slot.data.detailRevision; + let detail = await getContactChannelDetail(guid + "." + token, channel.id); + cur.data.channelDetail = detail; + cur.data.detailRevision = channel.data.detailRevision; } } cur.data.topicRevision = channel.data.topicRevision; diff --git a/net/web/src/context/useChannelContext.hook.js b/net/web/src/context/useChannelContext.hook.js index 2c1ef8db..3ecf8004 100644 --- a/net/web/src/context/useChannelContext.hook.js +++ b/net/web/src/context/useChannelContext.hook.js @@ -1,6 +1,6 @@ import { useEffect, useState, useRef } from 'react'; import { getChannels } from 'api/getChannels'; -import { getChannel } from 'api/getChannel'; +import { getChannelDetail } from 'api/getChannelDetail'; import { addChannel } from 'api/addChannel'; import { addChannelTopic } from 'api/addChannelTopic'; import { getChannelTopics } from 'api/getChannelTopics'; @@ -35,9 +35,9 @@ export function useChannelContext() { cur.data.detailRevision = channel.data.detailRevision; } else { - let slot = await getChannel(access.current, channel.id); - cur.data.channelDetail = slot.data.channelDetail; - cur.data.detailRevision = slot.data.detailRevision; + let detail = await getChannelDetail(access.current, channel.id); + cur.data.channelDetail = detail; + cur.data.detailRevision = channel.data.detailRevision; } } cur.data.topicRevision = channel.data.topicRevision;