Browse Source

further integration improvements (#5)

* further integration improvements

* fix search mocked resolver
Klaudiusz Dembler 4 years ago
parent
commit
b46a968ebb

+ 22 - 12
src/api/client/resolvers.ts

@@ -1,24 +1,34 @@
 import { GraphQLSchema } from 'graphql'
 import { delegateToSchema } from '@graphql-tools/delegate'
-import type { IResolvers } from '@graphql-tools/utils'
+import type { IResolvers, ISchemaLevelResolver } from '@graphql-tools/utils'
 import { TransformOrionViewsField, ORION_VIEWS_FIELD_NAME, RemoveQueryNodeViewsField } from './transforms'
 
+const createResolverWithoutVideoViewsField = (
+  schema: GraphQLSchema,
+  fieldName: string
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+): ISchemaLevelResolver<any, any> => {
+  return async (parent, args, context, info) =>
+    delegateToSchema({
+      schema,
+      operation: 'query',
+      fieldName,
+      args,
+      context,
+      info,
+      transforms: [RemoveQueryNodeViewsField],
+    })
+}
+
 export const queryNodeStitchingResolvers = (
   queryNodeSchema: GraphQLSchema,
   orionSchema: GraphQLSchema
 ): IResolvers => ({
   Query: {
-    videosConnection: async (parent, args, context, info) => {
-      return await delegateToSchema({
-        schema: queryNodeSchema,
-        operation: 'query',
-        fieldName: 'videosConnection',
-        args,
-        context,
-        info,
-        transforms: [RemoveQueryNodeViewsField],
-      })
-    },
+    videosConnection: createResolverWithoutVideoViewsField(queryNodeSchema, 'videosConnection'),
+    channel: createResolverWithoutVideoViewsField(queryNodeSchema, 'channel'),
+    search: createResolverWithoutVideoViewsField(queryNodeSchema, 'search'),
+    video: createResolverWithoutVideoViewsField(queryNodeSchema, 'video'),
   },
   Video: {
     // TODO: Resolve the views count in parallel to the videosConnection query

+ 7 - 7
src/api/queries/__generated__/GetChannel.ts

@@ -12,17 +12,17 @@ export interface GetChannel_channel_videos_category {
   id: string;
 }
 
-export interface GetChannel_channel_videos_media_location_HTTPVideoMediaLocation {
-  __typename: "HTTPVideoMediaLocation";
-  URL: string;
+export interface GetChannel_channel_videos_media_location_HttpMediaLocation {
+  __typename: "HttpMediaLocation";
+  url: string;
 }
 
-export interface GetChannel_channel_videos_media_location_JoystreamVideoMediaLocation {
-  __typename: "JoystreamVideoMediaLocation";
-  dataObjectID: string;
+export interface GetChannel_channel_videos_media_location_JoystreamMediaLocation {
+  __typename: "JoystreamMediaLocation";
+  dataObjectId: string;
 }
 
-export type GetChannel_channel_videos_media_location = GetChannel_channel_videos_media_location_HTTPVideoMediaLocation | GetChannel_channel_videos_media_location_JoystreamVideoMediaLocation;
+export type GetChannel_channel_videos_media_location = GetChannel_channel_videos_media_location_HttpMediaLocation | GetChannel_channel_videos_media_location_JoystreamMediaLocation;
 
 export interface GetChannel_channel_videos_media {
   __typename: "VideoMedia";

+ 7 - 7
src/api/queries/__generated__/GetFeaturedVideos.ts

@@ -12,17 +12,17 @@ export interface GetFeaturedVideos_featured_videos_category {
   id: string;
 }
 
-export interface GetFeaturedVideos_featured_videos_media_location_HTTPVideoMediaLocation {
-  __typename: "HTTPVideoMediaLocation";
-  URL: string;
+export interface GetFeaturedVideos_featured_videos_media_location_HttpMediaLocation {
+  __typename: "HttpMediaLocation";
+  url: string;
 }
 
-export interface GetFeaturedVideos_featured_videos_media_location_JoystreamVideoMediaLocation {
-  __typename: "JoystreamVideoMediaLocation";
-  dataObjectID: string;
+export interface GetFeaturedVideos_featured_videos_media_location_JoystreamMediaLocation {
+  __typename: "JoystreamMediaLocation";
+  dataObjectId: string;
 }
 
-export type GetFeaturedVideos_featured_videos_media_location = GetFeaturedVideos_featured_videos_media_location_HTTPVideoMediaLocation | GetFeaturedVideos_featured_videos_media_location_JoystreamVideoMediaLocation;
+export type GetFeaturedVideos_featured_videos_media_location = GetFeaturedVideos_featured_videos_media_location_HttpMediaLocation | GetFeaturedVideos_featured_videos_media_location_JoystreamMediaLocation;
 
 export interface GetFeaturedVideos_featured_videos_media {
   __typename: "VideoMedia";

+ 7 - 7
src/api/queries/__generated__/GetNewestVideos.ts

@@ -12,17 +12,17 @@ export interface GetNewestVideos_videosConnection_edges_node_category {
   id: string;
 }
 
-export interface GetNewestVideos_videosConnection_edges_node_media_location_HTTPVideoMediaLocation {
-  __typename: "HTTPVideoMediaLocation";
-  URL: string;
+export interface GetNewestVideos_videosConnection_edges_node_media_location_HttpMediaLocation {
+  __typename: "HttpMediaLocation";
+  url: string;
 }
 
-export interface GetNewestVideos_videosConnection_edges_node_media_location_JoystreamVideoMediaLocation {
-  __typename: "JoystreamVideoMediaLocation";
-  dataObjectID: string;
+export interface GetNewestVideos_videosConnection_edges_node_media_location_JoystreamMediaLocation {
+  __typename: "JoystreamMediaLocation";
+  dataObjectId: string;
 }
 
-export type GetNewestVideos_videosConnection_edges_node_media_location = GetNewestVideos_videosConnection_edges_node_media_location_HTTPVideoMediaLocation | GetNewestVideos_videosConnection_edges_node_media_location_JoystreamVideoMediaLocation;
+export type GetNewestVideos_videosConnection_edges_node_media_location = GetNewestVideos_videosConnection_edges_node_media_location_HttpMediaLocation | GetNewestVideos_videosConnection_edges_node_media_location_JoystreamMediaLocation;
 
 export interface GetNewestVideos_videosConnection_edges_node_media {
   __typename: "VideoMedia";

+ 14 - 14
src/api/queries/__generated__/GetVideo.ts

@@ -12,17 +12,17 @@ export interface GetVideo_video_category {
   id: string;
 }
 
-export interface GetVideo_video_media_location_HTTPVideoMediaLocation {
-  __typename: "HTTPVideoMediaLocation";
-  URL: string;
+export interface GetVideo_video_media_location_HttpMediaLocation {
+  __typename: "HttpMediaLocation";
+  url: string;
 }
 
-export interface GetVideo_video_media_location_JoystreamVideoMediaLocation {
-  __typename: "JoystreamVideoMediaLocation";
-  dataObjectID: string;
+export interface GetVideo_video_media_location_JoystreamMediaLocation {
+  __typename: "JoystreamMediaLocation";
+  dataObjectId: string;
 }
 
-export type GetVideo_video_media_location = GetVideo_video_media_location_HTTPVideoMediaLocation | GetVideo_video_media_location_JoystreamVideoMediaLocation;
+export type GetVideo_video_media_location = GetVideo_video_media_location_HttpMediaLocation | GetVideo_video_media_location_JoystreamMediaLocation;
 
 export interface GetVideo_video_media {
   __typename: "VideoMedia";
@@ -37,17 +37,17 @@ export interface GetVideo_video_channel_videos_category {
   id: string;
 }
 
-export interface GetVideo_video_channel_videos_media_location_HTTPVideoMediaLocation {
-  __typename: "HTTPVideoMediaLocation";
-  URL: string;
+export interface GetVideo_video_channel_videos_media_location_HttpMediaLocation {
+  __typename: "HttpMediaLocation";
+  url: string;
 }
 
-export interface GetVideo_video_channel_videos_media_location_JoystreamVideoMediaLocation {
-  __typename: "JoystreamVideoMediaLocation";
-  dataObjectID: string;
+export interface GetVideo_video_channel_videos_media_location_JoystreamMediaLocation {
+  __typename: "JoystreamMediaLocation";
+  dataObjectId: string;
 }
 
-export type GetVideo_video_channel_videos_media_location = GetVideo_video_channel_videos_media_location_HTTPVideoMediaLocation | GetVideo_video_channel_videos_media_location_JoystreamVideoMediaLocation;
+export type GetVideo_video_channel_videos_media_location = GetVideo_video_channel_videos_media_location_HttpMediaLocation | GetVideo_video_channel_videos_media_location_JoystreamMediaLocation;
 
 export interface GetVideo_video_channel_videos_media {
   __typename: "VideoMedia";

+ 9 - 9
src/api/queries/__generated__/Search.ts

@@ -12,17 +12,17 @@ export interface Search_search_item_Video_category {
   id: string;
 }
 
-export interface Search_search_item_Video_media_location_HTTPVideoMediaLocation {
-  __typename: "HTTPVideoMediaLocation";
-  URL: string;
+export interface Search_search_item_Video_media_location_HttpMediaLocation {
+  __typename: "HttpMediaLocation";
+  url: string;
 }
 
-export interface Search_search_item_Video_media_location_JoystreamVideoMediaLocation {
-  __typename: "JoystreamVideoMediaLocation";
-  dataObjectID: string;
+export interface Search_search_item_Video_media_location_JoystreamMediaLocation {
+  __typename: "JoystreamMediaLocation";
+  dataObjectId: string;
 }
 
-export type Search_search_item_Video_media_location = Search_search_item_Video_media_location_HTTPVideoMediaLocation | Search_search_item_Video_media_location_JoystreamVideoMediaLocation;
+export type Search_search_item_Video_media_location = Search_search_item_Video_media_location_HttpMediaLocation | Search_search_item_Video_media_location_JoystreamMediaLocation;
 
 export interface Search_search_item_Video_media {
   __typename: "VideoMedia";
@@ -64,7 +64,7 @@ export interface Search_search_item_Channel {
 export type Search_search_item = Search_search_item_Video | Search_search_item_Channel;
 
 export interface Search_search {
-  __typename: "FreeTextSearchResult";
+  __typename: "SearchFTSOutput";
   item: Search_search_item;
   rank: number;
 }
@@ -74,5 +74,5 @@ export interface Search {
 }
 
 export interface SearchVariables {
-  query_string: string;
+  text: string;
 }

+ 7 - 7
src/api/queries/__generated__/VideoFields.ts

@@ -12,17 +12,17 @@ export interface VideoFields_category {
   id: string;
 }
 
-export interface VideoFields_media_location_HTTPVideoMediaLocation {
-  __typename: "HTTPVideoMediaLocation";
-  URL: string;
+export interface VideoFields_media_location_HttpMediaLocation {
+  __typename: "HttpMediaLocation";
+  url: string;
 }
 
-export interface VideoFields_media_location_JoystreamVideoMediaLocation {
-  __typename: "JoystreamVideoMediaLocation";
-  dataObjectID: string;
+export interface VideoFields_media_location_JoystreamMediaLocation {
+  __typename: "JoystreamMediaLocation";
+  dataObjectId: string;
 }
 
-export type VideoFields_media_location = VideoFields_media_location_HTTPVideoMediaLocation | VideoFields_media_location_JoystreamVideoMediaLocation;
+export type VideoFields_media_location = VideoFields_media_location_HttpMediaLocation | VideoFields_media_location_JoystreamMediaLocation;
 
 export interface VideoFields_media {
   __typename: "VideoMedia";

+ 7 - 7
src/api/queries/__generated__/VideoMediaFields.ts

@@ -7,17 +7,17 @@
 // GraphQL fragment: VideoMediaFields
 // ====================================================
 
-export interface VideoMediaFields_location_HTTPVideoMediaLocation {
-  __typename: "HTTPVideoMediaLocation";
-  URL: string;
+export interface VideoMediaFields_location_HttpMediaLocation {
+  __typename: "HttpMediaLocation";
+  url: string;
 }
 
-export interface VideoMediaFields_location_JoystreamVideoMediaLocation {
-  __typename: "JoystreamVideoMediaLocation";
-  dataObjectID: string;
+export interface VideoMediaFields_location_JoystreamMediaLocation {
+  __typename: "JoystreamMediaLocation";
+  dataObjectId: string;
 }
 
-export type VideoMediaFields_location = VideoMediaFields_location_HTTPVideoMediaLocation | VideoMediaFields_location_JoystreamVideoMediaLocation;
+export type VideoMediaFields_location = VideoMediaFields_location_HttpMediaLocation | VideoMediaFields_location_JoystreamMediaLocation;
 
 export interface VideoMediaFields {
   __typename: "VideoMedia";

+ 1 - 1
src/api/queries/channels.ts

@@ -31,7 +31,7 @@ export const GET_NEWEST_CHANNELS = gql`
 
 export const GET_CHANNEL = gql`
   query GetChannel($id: ID!) {
-    channel(id: $id) {
+    channel(where: { id: $id }) {
       ...ChannelFields
       videos {
         ...VideoFields

+ 2 - 2
src/api/queries/search.ts

@@ -3,8 +3,8 @@ import { videoFieldsFragment } from './videos'
 import { channelFieldsFragment } from './channels'
 
 export const SEARCH = gql`
-  query Search($query_string: String!) {
-    search(query_string: $query_string) {
+  query Search($text: String!) {
+    search(text: $text) {
       item {
         ... on Video {
           ...VideoFields

+ 5 - 5
src/api/queries/videos.ts

@@ -6,11 +6,11 @@ const videoMediaFieldsFragment = gql`
     pixelHeight
     pixelWidth
     location {
-      ... on HTTPVideoMediaLocation {
-        URL
+      ... on HttpMediaLocation {
+        url
       }
-      ... on JoystreamVideoMediaLocation {
-        dataObjectID
+      ... on JoystreamMediaLocation {
+        dataObjectId
       }
     }
   }
@@ -75,7 +75,7 @@ export const GET_FEATURED_VIDEOS = gql`
 
 export const GET_VIDEO_WITH_CHANNEL_VIDEOS = gql`
   query GetVideo($id: ID!) {
-    video(id: $id) {
+    video(where: { id: $id }) {
       ...VideoFields
       channel {
         id

+ 29 - 13
src/api/schemas/extendedQueryNode.graphql

@@ -45,16 +45,16 @@ type Category {
   videos: [Video!]
 }
 
-type JoystreamVideoMediaLocation {
-  dataObjectID: String!
+type JoystreamMediaLocation {
+  dataObjectId: String!
 }
 
-type HTTPVideoMediaLocation {
-  URL: String!
+type HttpMediaLocation {
+  url: String!
 }
 
 # In the future we can add IPFS, Dat, etc.
-union MediaLocation = JoystreamVideoMediaLocation | HTTPVideoMediaLocation
+union MediaLocation = JoystreamMediaLocation | HttpMediaLocation
 
 # Mixed both encoding and containers, only having popular combos, may need to be changed later.
 enum VideoMediaEncoding {
@@ -145,12 +145,16 @@ type Video {
   license: License!
 }
 
-union FreeTextSearchResultItemType = Video | Channel
+union SearchResult = Video | Channel
 
-type FreeTextSearchResult {
-  item: FreeTextSearchResultItemType!
+type SearchFTSOutput {
+  item: SearchResult!
 
-  rank: Int!
+  rank: Float!
+
+  isTypeOf: String!
+
+  highlight: String!
 }
 
 type PageInfo {
@@ -166,6 +170,10 @@ input ChannelWhereInput {
   isPublic_eq: Boolean
 }
 
+input ChannelWhereUniqueInput {
+  id: ID!
+}
+
 enum ChannelOrderByInput {
   createdAt_ASC
   createdAt_DESC
@@ -182,12 +190,20 @@ type ChannelConnection {
   totalCount: Int!
 }
 
+input CategoryWhereUniqueInput {
+  id: ID!
+}
+
 input VideoWhereInput {
   categoryId_eq: ID
   isCurated_eq: Boolean
   isPublic_eq: Boolean
 }
 
+input VideoWhereUniqueInput {
+  id: ID!
+}
+
 enum VideoOrderByInput {
   createdAt_ASC
   createdAt_DESC
@@ -211,7 +227,7 @@ type VideoViewsInfo {
 
 type Query {
   # Lookup a channel by its ID
-  channel(id: ID!): Channel
+  channel(where: ChannelWhereUniqueInput!): Channel
 
   # List all channel by given constraints
   channelsConnection(
@@ -222,13 +238,13 @@ type Query {
   ): ChannelConnection!
 
   # Lookup a channel by its ID
-  category(id: ID!): Category
+  category(where: CategoryWhereUniqueInput!): Category
 
   # List all categories
   categories: [Category!]!
 
   # Lookup video by its ID
-  video(id: ID!): Video
+  video(where: VideoWhereUniqueInput!): Video
 
   # List all videos by given constraints
   videosConnection(first: Int, after: String, where: VideoWhereInput, orderBy: VideoOrderByInput): VideoConnection!
@@ -237,7 +253,7 @@ type Query {
   featured_videos: [Video!]!
 
   # Free text search across videos and channels
-  search(query_string: String!): [FreeTextSearchResult!]!
+  search(limit: Int, text: String!): [SearchFTSOutput!]!
 }
 
 type Mutation {

+ 4 - 0
src/config/urls.ts

@@ -4,9 +4,13 @@ const readEnv = (name: string): string | undefined => {
 
 const envQueryNodeUrl = readEnv('REACT_APP_QUERY_NODE_URL')
 const envOrionUrl = readEnv('REACT_APP_ORION_URL')
+const envStorageNodeUrl = readEnv('REACT_APP_STORAGE_NODE_URL')
 
 export const MOCK_QUERY_NODE_GRAPHQL_URL = '/query-node-graphql'
 export const QUERY_NODE_GRAPHQL_URL = envQueryNodeUrl || MOCK_QUERY_NODE_GRAPHQL_URL
 
 export const MOCK_ORION_GRAPHQL_URL = '/orion-graphql'
 export const ORION_GRAPHQL_URL = envOrionUrl || MOCK_ORION_GRAPHQL_URL
+
+export const MOCK_STORAGE_NODE_URL = 'https://babylon-sub-storage-1.joystream.app/storage/asset/v0/'
+export const STORAGE_NODE_URL = envStorageNodeUrl || MOCK_STORAGE_NODE_URL

+ 2 - 2
src/mocking/data/mockCoverVideo.ts

@@ -17,11 +17,11 @@ export const mockCoverVideoMedia: MockVideoMedia = {
   ...rawCoverVideo.videoMedia,
   __typename: 'VideoMedia',
   location: {
-    __typename: 'HTTPVideoMediaLocation',
+    __typename: 'HttpMediaLocation',
     ...rawCoverVideo.videoMedia.location,
   },
   coverCutLocation: {
-    __typename: 'HTTPVideoMediaLocation',
+    __typename: 'HttpMediaLocation',
     ...rawCoverVideo.videoMedia.coverCutLocation,
   },
 }

+ 1 - 1
src/mocking/data/mockVideosMedia.ts

@@ -11,7 +11,7 @@ const mockVideosMedia: MockVideoMedia[] = rawVideosMedia.map((rawVideoMedia) =>
     ...rawVideoMedia,
     __typename: 'VideoMedia',
     location: {
-      __typename: 'HTTPVideoMediaLocation',
+      __typename: 'HttpMediaLocation',
       ...rawVideoMedia.location,
     },
   }

+ 2 - 2
src/mocking/data/raw/coverVideo.json

@@ -14,8 +14,8 @@
     "pixelHeight": 1440,
     "duration": 29,
     "size": 44851016,
-    "location": { "URL": "https://eu-central-1.linodeobjects.com/atlas-assets/cover-video/video.webm" },
-    "coverCutLocation": { "URL": "https://eu-central-1.linodeobjects.com/atlas-assets/cover-video/cut.mp4" }
+    "location": { "url": "https://eu-central-1.linodeobjects.com/atlas-assets/cover-video/video.webm" },
+    "coverCutLocation": { "url": "https://eu-central-1.linodeobjects.com/atlas-assets/cover-video/cut.mp4" }
   },
   "channel": {
     "id": "d458e199-e1ae-4ccc-9f42-0ff8fbf9ab81",

+ 9 - 9
src/mocking/data/raw/videosMedia.json

@@ -6,7 +6,7 @@
     "pixelHeight": 1440,
     "duration": 29,
     "size": 44851016,
-    "location": { "URL": "https://eu-central-1.linodeobjects.com/atlas-assets/videos/1.mp4" }
+    "location": { "url": "https://eu-central-1.linodeobjects.com/atlas-assets/videos/1.mp4" }
   },
   {
     "id": "acbfd83a-66d8-4275-9270-369b2cab8ee0",
@@ -15,7 +15,7 @@
     "pixelHeight": 720,
     "duration": 100,
     "size": 71928640,
-    "location": { "URL": "https://eu-central-1.linodeobjects.com/atlas-assets/videos/2.m4v" }
+    "location": { "url": "https://eu-central-1.linodeobjects.com/atlas-assets/videos/2.m4v" }
   },
   {
     "id": "137536b1-1965-416d-aecd-6356b536bc18",
@@ -24,7 +24,7 @@
     "pixelHeight": 720,
     "duration": 162,
     "size": 48997777,
-    "location": { "URL": "https://eu-central-1.linodeobjects.com/atlas-assets/videos/3.mp4" }
+    "location": { "url": "https://eu-central-1.linodeobjects.com/atlas-assets/videos/3.mp4" }
   },
   {
     "id": "0de1ac1a-d1bd-4a08-8115-a800cd5f1fc1",
@@ -33,7 +33,7 @@
     "pixelHeight": 1080,
     "duration": 103,
     "size": 6959818,
-    "location": { "URL": "https://eu-central-1.linodeobjects.com/atlas-assets/videos/4.webm" }
+    "location": { "url": "https://eu-central-1.linodeobjects.com/atlas-assets/videos/4.webm" }
   },
   {
     "id": "a0a09887-7e1b-4a4f-b386-09f02bb59e2c",
@@ -42,7 +42,7 @@
     "pixelHeight": 1080,
     "duration": 47,
     "size": 34452169,
-    "location": { "URL": "https://eu-central-1.linodeobjects.com/atlas-assets/videos/5.mp4" }
+    "location": { "url": "https://eu-central-1.linodeobjects.com/atlas-assets/videos/5.mp4" }
   },
   {
     "id": "2c6e01be-52e3-469f-8e5e-ad4cac864fa9",
@@ -51,7 +51,7 @@
     "pixelHeight": 1080,
     "duration": 59,
     "size": 37488451,
-    "location": { "URL": "https://eu-central-1.linodeobjects.com/atlas-assets/videos/6.mp4" }
+    "location": { "url": "https://eu-central-1.linodeobjects.com/atlas-assets/videos/6.mp4" }
   },
   {
     "id": "ed247bbf-aa89-4377-bfce-b055e4b05cb9",
@@ -60,7 +60,7 @@
     "pixelHeight": 480,
     "duration": 664,
     "size": 68512128,
-    "location": { "URL": "https://eu-central-1.linodeobjects.com/atlas-assets/videos/7.mp4" }
+    "location": { "url": "https://eu-central-1.linodeobjects.com/atlas-assets/videos/7.mp4" }
   },
   {
     "id": "46eda8d5-9a78-41c3-9ae7-72b5df936c69",
@@ -69,7 +69,7 @@
     "pixelHeight": 1080,
     "duration": 23,
     "size": 15223013,
-    "location": { "URL": "https://eu-central-1.linodeobjects.com/atlas-assets/videos/8.mp4" }
+    "location": { "url": "https://eu-central-1.linodeobjects.com/atlas-assets/videos/8.mp4" }
   },
   {
     "id": "2659dabe-928f-4789-ac92-13325acffa3c",
@@ -78,6 +78,6 @@
     "pixelHeight": 720,
     "duration": 403,
     "size": 295780291,
-    "location": { "URL": "https://eu-central-1.linodeobjects.com/atlas-assets/videos/9.m4v" }
+    "location": { "url": "https://eu-central-1.linodeobjects.com/atlas-assets/videos/9.m4v" }
   }
 ]

+ 2 - 2
src/mocking/server/data.ts

@@ -23,7 +23,7 @@ export const createMockData = (server: MirageJSServer) => {
 
   const videoMedias = mockVideosMedia.map((videoMedia) => {
     // FIXME: This suffers from the same behaviour as the search resolver - all the returned items have the same location
-    const location = server.schema.create('HTTPVideoMediaLocation', {
+    const location = server.schema.create('HttpMediaLocation', {
       id: faker.random.uuid(),
       ...videoMedia.location,
     })
@@ -61,7 +61,7 @@ const createCoverVideoData = (server: MirageJSServer, categories: unknown[]) =>
     ...mockCoverVideoChannel,
   })
 
-  const location = server.schema.create('HTTPVideoMediaLocation', {
+  const location = server.schema.create('HttpMediaLocation', {
     id: faker.random.uuid(),
     ...mockCoverVideoMedia.location,
   })

+ 4 - 1
src/mocking/server/index.ts

@@ -10,6 +10,7 @@ import {
   channelsResolver,
   featuredVideosResolver,
   searchResolver,
+  videoResolver,
   videosResolver,
   videoViewsResolver,
 } from './resolvers'
@@ -26,13 +27,15 @@ createServer({
     const queryNodeHandler = createGraphQLHandler(extendedQueryNodeSchema, this.schema, {
       resolvers: {
         Query: {
+          video: videoResolver,
           videosConnection: videosResolver,
           featured_videos: featuredVideosResolver,
+          channel: channelsResolver,
           channelsConnection: channelsResolver,
           search: searchResolver,
         },
         // TODO: remove these once the MirageJS bug gets resolved: https://github.com/miragejs/graphql/issues/16
-        FreeTextSearchResult: {
+        SearchFTSOutput: {
           item: ({ item }: any) => item,
         },
         VideoMedia: {

+ 31 - 4
src/mocking/server/resolvers.ts

@@ -25,6 +25,18 @@ type VideoQueryArgs = {
   } | null
 }
 
+type UniqueArgs = {
+  where: { id: string }
+}
+
+export const videoResolver: QueryResolver<UniqueArgs, VideoFields> = (obj, args, context, info) => {
+  const resolverArgs = {
+    id: args.where.id,
+  }
+
+  return mirageGraphQLFieldResolver(obj, resolverArgs, context, info)
+}
+
 export const videosResolver: QueryResolver<VideoQueryArgs, GetNewestVideos_videosConnection> = (
   obj,
   args,
@@ -54,6 +66,14 @@ export const featuredVideosResolver: QueryResolver<object, VideoFields[]> = (...
   return videos.filter((_, idx) => FEATURED_VIDEOS_INDEXES.includes(idx))
 }
 
+export const channelResolver: QueryResolver<UniqueArgs, ChannelFields> = (obj, args, context, info) => {
+  const resolverArgs = {
+    id: args.where.id,
+  }
+
+  return mirageGraphQLFieldResolver(obj, resolverArgs, context, info)
+}
+
 export const channelsResolver: QueryResolver<GetNewestChannelsVariables, GetNewestChannels_channelsConnection> = (
   obj,
   args,
@@ -70,7 +90,7 @@ export const channelsResolver: QueryResolver<GetNewestChannelsVariables, GetNewe
 }
 
 // FIXME: This resolver is currently broken and returns the same result n times instead of the correct result.
-export const searchResolver: QueryResolver<SearchVariables, Search_search[]> = (_, { query_string }, context) => {
+export const searchResolver: QueryResolver<SearchVariables, Search_search[]> = (_, { text }, context) => {
   const { mirageSchema: schema } = context
   const videos = getRecords({ name: 'Video' }, {}, schema) as VideoFields[]
   const channels = getRecords({ name: 'Channel' }, {}, schema) as ChannelFields[]
@@ -78,20 +98,19 @@ export const searchResolver: QueryResolver<SearchVariables, Search_search[]> = (
   const items = [...videos, ...channels]
 
   let rankCount = 0
-  const matchQueryStr = (str: string) => str.includes(query_string) || query_string.includes(str)
+  const matchQueryStr = (str: string) => str.includes(text) || text.includes(str)
 
   const relevantItems = items.reduce((acc, item) => {
     const matched =
       item.__typename === 'Channel'
         ? matchQueryStr(item.handle)
         : matchQueryStr(item.description) || matchQueryStr(item.title)
-
     if (!matched) {
       return acc
     }
 
     const result: Search_search = {
-      __typename: 'FreeTextSearchResult',
+      __typename: 'SearchFTSOutput',
       item,
       rank: rankCount++,
     }
@@ -111,6 +130,14 @@ export const videoViewsResolver: QueryResolver<VideoViewsArgs> = (obj, args, con
 
 export const addVideoViewResolver: QueryResolver<VideoViewsArgs> = (obj, args, context, info) => {
   const videoInfo = context.mirageSchema.videoViewsInfos.find(args.videoID)
+  if (!videoInfo) {
+    const videoInfo = context.mirageSchema.videoViewsInfos.create({
+      id: args.videoID,
+      views: 1,
+    })
+
+    return videoInfo
+  }
   videoInfo.update({
     views: videoInfo.views + 1,
   })

+ 4 - 0
src/shared/components/InfiniteVideoGrid/InfiniteVideoGrid.tsx

@@ -141,6 +141,10 @@ const InfiniteVideoGrid: React.FC<InfiniteVideoGridProps> = ({
     </>
   )
 
+  if (displayedVideos.length <= 0 && placeholdersCount <= 0) {
+    return null
+  }
+
   return (
     <section className={className}>
       {title && <Title>{title}</Title>}

+ 7 - 1
src/shared/components/VideoPlayer/videoJsPlayer.ts

@@ -3,6 +3,7 @@ import videojs, { VideoJsPlayer, VideoJsPlayerOptions } from 'video.js'
 import 'video.js/dist/video-js.css'
 
 import { VideoFields_media_location } from '@/api/queries/__generated__/VideoFields'
+import { STORAGE_NODE_URL } from '@/config/urls'
 
 export type VideoJsConfig = {
   src: VideoFields_media_location
@@ -14,12 +15,17 @@ export type VideoJsConfig = {
   posterUrl?: string
 }
 
+const createJoystreamStorageUrl = (dataObjectId: string) => {
+  const url = new URL(dataObjectId, STORAGE_NODE_URL)
+  return url.href
+}
+
 type VideoJsPlayerHook = (config: VideoJsConfig) => [VideoJsPlayer | null, RefObject<HTMLVideoElement>]
 export const useVideoJsPlayer: VideoJsPlayerHook = ({ fill, fluid, height, src, width, muted = false, posterUrl }) => {
   const playerRef = useRef<HTMLVideoElement>(null)
   const [player, setPlayer] = useState<VideoJsPlayer | null>(null)
 
-  const parsedSource = src.__typename === 'HTTPVideoMediaLocation' ? src.URL : 'TODO'
+  const parsedSource = src.__typename === 'HttpMediaLocation' ? src.url : createJoystreamStorageUrl(src.dataObjectId)
 
   useEffect(() => {
     const videoJsOptions: VideoJsPlayerOptions = {

+ 1 - 1
src/views/SearchView/SearchView.tsx

@@ -18,7 +18,7 @@ const tabs = ['all results', 'videos', 'channels']
 
 const SearchView: React.FC<SearchViewProps> = ({ search = '' }) => {
   const [selectedIndex, setSelectedIndex] = useState(0)
-  const { data, loading, error } = useQuery<Search, SearchVariables>(SEARCH, { variables: { query_string: search } })
+  const { data, loading, error } = useQuery<Search, SearchVariables>(SEARCH, { variables: { text: search } })
 
   const getChannelsAndVideos = (loading: boolean, data: Search | undefined) => {
     if (loading || !data?.search) {