refactor to prepare for channel peak data

This commit is contained in:
Roland Osborne 2022-05-09 12:53:00 -07:00
parent 8f8cd6eb4d
commit 0416389be0
14 changed files with 86 additions and 193 deletions

View File

@ -2118,7 +2118,7 @@ paths:
tags: tags:
- content - content
description: Get details of channel. description: Get details of channel.
operationId: get-channel operationId: get-channel-detail
security: security:
- bearerAuth: [] - bearerAuth: []
parameters: parameters:
@ -2134,7 +2134,7 @@ paths:
content: content:
application/json: application/json:
schema: schema:
$ref: '#/components/schemas/Channel' $ref: '#/components/schemas/ChannelDetail'
'401': '401':
description: invalid password description: invalid password
'404': '404':
@ -2472,43 +2472,6 @@ paths:
schema: schema:
$ref: '#/components/schemas/Subject' $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}: /content/channels/{channelId}/topics/{topicId}:
delete: delete:
tags: tags:
@ -3374,7 +3337,8 @@ components:
node: node:
type: string type: string
revision: revision:
type: int64 type: integer
format: int64
CardDetail: CardDetail:
type: object type: object
@ -3500,7 +3464,7 @@ components:
$ref: '#/components/schemas/ChannelContacts' $ref: '#/components/schemas/ChannelContacts'
members: members:
type: array type: array
item: items:
type: string type: string
ChannelContacts: ChannelContacts:
@ -3860,7 +3824,3 @@ components:

View File

@ -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))
}
}

View File

@ -5,6 +5,7 @@ import (
"errors" "errors"
"strconv" "strconv"
"net/http" "net/http"
"gorm.io/gorm"
"encoding/json" "encoding/json"
"databag/internal/store" "databag/internal/store"
) )
@ -101,7 +102,9 @@ func GetChannels(w http.ResponseWriter, r *http.Request) {
account := &card.Account account := &card.Account
var slots []store.ChannelSlot var slots []store.ChannelSlot
if channelRevisionSet { 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) ErrResponse(w, http.StatusInternalServerError, err)
return return
} }

View File

@ -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 { if slot.Channel == nil {
return &Channel{ return nil
Id: slot.ChannelSlotId,
Revision: slot.Revision,
}
} }
var contacts *ChannelContacts var contacts *ChannelContacts
@ -192,20 +189,32 @@ func getChannelModel(slot *store.ChannelSlot, showData bool, showList bool) *Cha
members = append(members, card.Guid) members = append(members, card.Guid)
} }
return &Channel{ return &ChannelDetail{
Id: slot.ChannelSlotId,
Revision: slot.Revision,
Data: &ChannelData {
DetailRevision: slot.Channel.DetailRevision,
TopicRevision: slot.Channel.TopicRevision,
ChannelDetail: &ChannelDetail{
DataType: slot.Channel.DataType, DataType: slot.Channel.DataType,
Data: slot.Channel.Data, Data: slot.Channel.Data,
Created: slot.Channel.Created, Created: slot.Channel.Created,
Updated: slot.Channel.Updated, Updated: slot.Channel.Updated,
Contacts: contacts, Contacts: contacts,
Members: members, 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: getChannelDetailModel(slot, showList),
}, },
} }
} }

View File

@ -539,10 +539,10 @@ var routes = Routes{
}, },
Route{ Route{
"GetChannel", "GetChannelDetail",
strings.ToUpper("Get"), strings.ToUpper("Get"),
"/content/channels/{channelId}/detail", "/content/channels/{channelId}/detail",
GetChannel, GetChannelDetail,
}, },
Route{ Route{

View File

@ -215,16 +215,16 @@ func (c *TestContactData) UpdateContactChannel(storeChannel *TestChannel, channe
token := c.card.Data.CardProfile.Guid + "." + c.card.Data.CardDetail.Token token := c.card.Data.CardProfile.Guid + "." + c.card.Data.CardDetail.Token
params := &TestApiParams{ query: "/channel/{channelId}", path: map[string]string{ "channelId": channel.Id }, params := &TestApiParams{ query: "/channel/{channelId}", path: map[string]string{ "channelId": channel.Id },
tokenType: APP_TOKENCONTACT, token: token } tokenType: APP_TOKENCONTACT, token: token }
channel := Channel{} detail := &ChannelDetail{}
response := &TestApiResponse{ data: &channel } response := &TestApiResponse{ data: &detail }
if err = TestApiRequest(GetChannel, params, response); err != nil { if err = TestApiRequest(GetChannelDetail, params, response); err != nil {
return return
} }
if channel.Data == nil { if channel.Data == nil {
err = errors.New("channel removed during update") err = errors.New("channel removed during update")
return return
} }
storeChannel.channel.Data.ChannelDetail = channel.Data.ChannelDetail storeChannel.channel.Data.ChannelDetail = detail
storeChannel.channel.Data.DetailRevision = channel.Data.DetailRevision 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 { } else if storeChannel.channel.Data.DetailRevision != channel.Data.DetailRevision {
params := &TestApiParams{ query: "/channel/{channelId}", path: map[string]string{ "channelId": channel.Id }, params := &TestApiParams{ query: "/channel/{channelId}", path: map[string]string{ "channelId": channel.Id },
tokenType: APP_TOKENAGENT, token: a.token } tokenType: APP_TOKENAGENT, token: a.token }
channel := Channel{} detail := &ChannelDetail{}
response := &TestApiResponse{ data: &channel } response := &TestApiResponse{ data: &detail }
if err = TestApiRequest(GetChannel, params, response); err != nil { if err = TestApiRequest(GetChannelDetail, params, response); err != nil {
return return
} }
if channel.Data == nil { if channel.Data == nil {
err = errors.New("channel removed during update") err = errors.New("channel removed during update")
return return
} }
storeChannel.channel.Data.ChannelDetail = channel.Data.ChannelDetail storeChannel.channel.Data.ChannelDetail = detail
storeChannel.channel.Data.DetailRevision = channel.Data.DetailRevision storeChannel.channel.Data.DetailRevision = channel.Data.DetailRevision
} }
} }

View File

@ -9,6 +9,7 @@ import (
func TestChannelShare(t *testing.T) { func TestChannelShare(t *testing.T) {
var subject *Subject var subject *Subject
var channel *Channel var channel *Channel
var detail *ChannelDetail
var channels *[]Channel var channels *[]Channel
var cards *[]Card var cards *[]Card
aRevision := make(map[string][]string) aRevision := make(map[string][]string)
@ -79,13 +80,13 @@ func TestChannelShare(t *testing.T) {
assert.Nil(t, (*channels)[0].Data) assert.Nil(t, (*channels)[0].Data)
// get discovered channel // get discovered channel
channel = &Channel{} detail = &ChannelDetail{}
assert.NoError(t, ApiTestMsg(GetChannel, "GET", "/content/channels/{channelId}", assert.NoError(t, ApiTestMsg(GetChannelDetail, "GET", "/content/channels/{channelId}/detail",
&params, nil, APP_TOKENCONTACT, set.B.A.Token, channel, nil)) &params, nil, APP_TOKENCONTACT, set.B.A.Token, detail, nil))
assert.Equal(t, "channeldatatype", channel.Data.ChannelDetail.DataType) assert.Equal(t, "channeldatatype", detail.DataType)
assert.Equal(t, 1, len(channel.Data.ChannelDetail.Members)) assert.Equal(t, 1, len(detail.Members))
assert.Equal(t, set.B.Guid, channel.Data.ChannelDetail.Members[0]) assert.Equal(t, set.B.Guid, detail.Members[0])
assert.Nil(t, channel.Data.ChannelDetail.Contacts) assert.Nil(t, detail.Contacts)
// get revision // get revision
aRev = GetTestRevision(set.A.Revisions) aRev = GetTestRevision(set.A.Revisions)
@ -150,12 +151,12 @@ func TestChannelShare(t *testing.T) {
assert.Equal(t, 1, len(*channels)) assert.Equal(t, 1, len(*channels))
// get discovered channel // get discovered channel
channel = &Channel{} detail = &ChannelDetail{}
assert.NoError(t, ApiTestMsg(GetChannel, "GET", "/content/channels/{channelId}", assert.NoError(t, ApiTestMsg(GetChannelDetail, "GET", "/content/channels/{channelId}/detail",
&params, nil, APP_TOKENCONTACT, set.C.A.Token, channel, nil)) &params, nil, APP_TOKENCONTACT, set.C.A.Token, detail, nil))
assert.Equal(t, "channeldatatype", channel.Data.ChannelDetail.DataType) assert.Equal(t, "channeldatatype", detail.DataType)
assert.Equal(t, 2, len(channel.Data.ChannelDetail.Members)) assert.Equal(t, 2, len(detail.Members))
assert.Nil(t, channel.Data.ChannelDetail.Contacts) assert.Nil(t, detail.Contacts)
// reset notification // reset notification
GetTestRevision(set.B.Revisions) GetTestRevision(set.B.Revisions)

View File

@ -12,6 +12,7 @@ import (
func TestTopicShare(t *testing.T) { func TestTopicShare(t *testing.T) {
var topic *Topic var topic *Topic
var channel *Channel var channel *Channel
var detail *ChannelDetail
var subject *Subject var subject *Subject
params := make(map[string]string) params := make(map[string]string)
header := make(map[string][]string) header := make(map[string][]string)
@ -43,17 +44,17 @@ func TestTopicShare(t *testing.T) {
&params, nil, APP_TOKENAGENT, set.A.Token, nil, nil)) &params, nil, APP_TOKENAGENT, set.A.Token, nil, nil))
// view channel // view channel
channel = &Channel{} detail = &ChannelDetail{}
assert.NoError(t, ApiTestMsg(GetChannel, "GET", "/content/channels/{channelId}", assert.NoError(t, ApiTestMsg(GetChannelDetail, "GET", "/content/channels/{channelId}/detail",
&params, nil, APP_TOKENAGENT, set.A.Token, channel, nil)) &params, nil, APP_TOKENAGENT, set.A.Token, detail, nil))
assert.NotNil(t, detail);
detail = &ChannelDetail{}
assert.NoError(t, ApiTestMsg(GetChannelDetail, "GET", "/content/channels/{channelId}/detail",
&params, nil, APP_TOKENCONTACT, set.B.A.Token, detail, nil))
assert.NotNil(t, channel.Data.ChannelDetail); assert.NotNil(t, channel.Data.ChannelDetail);
channel = &Channel{} detail = &ChannelDetail{}
assert.NoError(t, ApiTestMsg(GetChannel, "GET", "/content/channels/{channelId}", assert.NoError(t, ApiTestMsg(GetChannelDetail, "GET", "/content/channels/{channelId}/detail",
&params, nil, APP_TOKENCONTACT, set.B.A.Token, channel, nil)) &params, 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}",
&params, nil, APP_TOKENCONTACT, set.B.A.Token, channel, nil))
assert.NotNil(t, channel.Data.ChannelDetail); assert.NotNil(t, channel.Data.ChannelDetail);
params["field"] = "nested.image" params["field"] = "nested.image"
data, header, err = ApiTestData(GetChannelSubjectField, "GET", "/content/channels/{channelId}/subject/{field}", data, header, err = ApiTestData(GetChannelSubjectField, "GET", "/content/channels/{channelId}/subject/{field}",

View File

@ -18,13 +18,13 @@ export function VideoAsset({ thumbUrl, lqUrl, hdUrl }) {
}, [thumbUrl, hdUrl, lqUrl]); }, [thumbUrl, hdUrl, lqUrl]);
const onFullScreen = () => { const onFullScreen = () => {
updateState({ fullscreen: true, modalUrl: hdUrl, playing: false, url: null }); updateState({ inline: false, popout: true, popoutUrl: hdUrl, playing: false, inlineUrl: null });
} }
const CenterButton = () => { const CenterButton = () => {
if (!state.loaded) { if (!state.inline) {
return ( return (
<div onClick={() => updateState({ loaded: true, url: lqUrl, controls: false, paused: true, playing: false })}> <div onClick={() => updateState({ inline: true, inlineUrl: lqUrl, playing: false })}>
<SelectOutlined style={{ fontSize: 48, color: '#eeeeee', cursor: 'pointer' }} /> <SelectOutlined style={{ fontSize: 48, color: '#eeeeee', cursor: 'pointer' }} />
</div> </div>
) )
@ -46,15 +46,12 @@ export function VideoAsset({ thumbUrl, lqUrl, hdUrl }) {
} }
const Controls = () => { const Controls = () => {
if (state.controls) {
return <></>;
}
return ( return (
<div> <div>
<div class="control"> <div class="control">
<CenterButton /> <CenterButton />
</div> </div>
<div class="fullscreen" onClick={() => onFullScreen()}> <div class="expand" onClick={() => onFullScreen()}>
<ExpandOutlined style={{ fontSize: 24, color: '#eeeeee', cursor: 'pointer' }} /> <ExpandOutlined style={{ fontSize: 24, color: '#eeeeee', cursor: 'pointer' }} />
</div> </div>
</div> </div>
@ -72,12 +69,12 @@ export function VideoAsset({ thumbUrl, lqUrl, hdUrl }) {
}} }}
</ReactResizeDetector> </ReactResizeDetector>
<div class="player" style={{ width: state.width, height: state.height }}> <div class="player" style={{ width: state.width, height: state.height }}>
<ReactPlayer ref={player} controls={state.controls} playing={state.playing} <ReactPlayer ref={player} controls={false} playing={state.playing}
height="100%" width="100%" url={state.url} /> height="100%" width="100%" url={state.inlineUrl} />
<Controls /> <Controls />
</div> </div>
<Modal visible={state.fullscreen} width={'60%'} bodyStyle={{ paddingBottom: 0, paddingTop: 6, paddingLeft: 6, paddingRight: 6, backgroundColor: '#dddddd' }} footer={null} destroyOnClose={true} closable={false} onCancel={() => { updateState({ fullscreen: false, modalUrl: null })}}> <Modal visible={state.popout} width={'60%'} bodyStyle={{ paddingBottom: 0, paddingTop: 6, paddingLeft: 6, paddingRight: 6, backgroundColor: '#dddddd' }} footer={null} destroyOnClose={true} closable={false} onCancel={() => { updateState({ popout: false })}}>
<ReactPlayer controls={true} height="100%" width="100%" url={state.modalUrl} /> <ReactPlayer controls={true} height="100%" width="100%" url={state.popoutUrl} />
</Modal> </Modal>
</VideoAssetWrapper> </VideoAssetWrapper>
) )

View File

@ -16,7 +16,7 @@ export const VideoAssetWrapper = styled.div`
visibility: visible; visibility: visible;
} }
.player:hover .fullscreen { .player:hover .expand {
visibility: visible; visibility: visible;
} }
@ -38,7 +38,7 @@ export const VideoAssetWrapper = styled.div`
opacity: 0.5; opacity: 0.5;
} }
.fullscreen { .expand {
padding-right: 2px; padding-right: 2px;
visibility: hidden; visibility: hidden;
position: absolute; position: absolute;

View File

@ -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()
}

View File

@ -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()
}

View File

@ -1,6 +1,6 @@
import { useEffect, useState, useRef } from 'react'; import { useEffect, useState, useRef } from 'react';
import { getContactChannels } from 'api/getContactChannels'; import { getContactChannels } from 'api/getContactChannels';
import { getContactChannel } from 'api/getContactChannel'; import { getContactChannelDetail } from 'api/getContactChannelDetail';
import { getContactProfile } from 'api/getContactProfile'; import { getContactProfile } from 'api/getContactProfile';
import { setCardProfile } from 'api/setCardProfile'; import { setCardProfile } from 'api/setCardProfile';
import { getCards } from 'api/getCards'; import { getCards } from 'api/getCards';
@ -119,9 +119,9 @@ export function useCardContext() {
cur.data.detailRevision = channel.data.detailRevision; cur.data.detailRevision = channel.data.detailRevision;
} }
else { else {
let slot = await getContactChannel(guid + "." + token, channel.id); let detail = await getContactChannelDetail(guid + "." + token, channel.id);
cur.data.channelDetail = slot.data.channelDetail; cur.data.channelDetail = detail;
cur.data.detailRevision = slot.data.detailRevision; cur.data.detailRevision = channel.data.detailRevision;
} }
} }
cur.data.topicRevision = channel.data.topicRevision; cur.data.topicRevision = channel.data.topicRevision;

View File

@ -1,6 +1,6 @@
import { useEffect, useState, useRef } from 'react'; import { useEffect, useState, useRef } from 'react';
import { getChannels } from 'api/getChannels'; import { getChannels } from 'api/getChannels';
import { getChannel } from 'api/getChannel'; import { getChannelDetail } from 'api/getChannelDetail';
import { addChannel } from 'api/addChannel'; import { addChannel } from 'api/addChannel';
import { addChannelTopic } from 'api/addChannelTopic'; import { addChannelTopic } from 'api/addChannelTopic';
import { getChannelTopics } from 'api/getChannelTopics'; import { getChannelTopics } from 'api/getChannelTopics';
@ -35,9 +35,9 @@ export function useChannelContext() {
cur.data.detailRevision = channel.data.detailRevision; cur.data.detailRevision = channel.data.detailRevision;
} }
else { else {
let slot = await getChannel(access.current, channel.id); let detail = await getChannelDetail(access.current, channel.id);
cur.data.channelDetail = slot.data.channelDetail; cur.data.channelDetail = detail;
cur.data.detailRevision = slot.data.detailRevision; cur.data.detailRevision = channel.data.detailRevision;
} }
} }
cur.data.topicRevision = channel.data.topicRevision; cur.data.topicRevision = channel.data.topicRevision;