Kaynağa Gözat

format selector

Tomi Cvetic 4 yıl önce
ebeveyn
işleme
3dbb2ada38

+ 4 - 0
backend/src/file/resolvers.ts

@@ -49,6 +49,10 @@ export const resolvers: IResolvers = {
       try {
         await fs.promises.unlink(file.path)
       } catch (error) {}
+      try {
+        await fs.promises.unlink(file.thumbnail)
+      } catch (error) {}
+
       return context.db.mutation.deleteFile({ where: { id } })
     },
   },

+ 9 - 13
backend/src/file/utils.ts

@@ -40,9 +40,7 @@ export function createThumbnail(image: string, thumbnail: string) {
     .toFile(thumbnail)
 }
 
-export function videoScreenshot(video: string, path: [string, string]) {
-  const filePath = path.join('/')
-  const thumbnail = `${filePath}.thmb`
+export function videoScreenshot(video: string, path: string) {
   return new Promise((resolve, reject) => {
     // ffmpeg(video)
     //   .screenshot({ folder, filename: filename, ...screenshotOptions })
@@ -52,16 +50,15 @@ export function videoScreenshot(video: string, path: [string, string]) {
     //   })
     //   .on('error', (error) => reject(error))
     ffmpeg(video)
+      .inputOption('-itsscale', '0.15')
       .outputOption(
         '-vf',
-        'fps=10,scale=200:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse',
+        'fps=5,scale=200:-1:flags=lanczos,split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse',
         '-loop',
-        '0',
-        '-t',
-        '3'
+        '-1'
       )
-      .save(thumbnail)
-      .on('end', () => resolve(thumbnail))
+      .save(path)
+      .on('end', () => resolve(path))
       .on('error', (error) => reject(error))
   })
 }
@@ -95,18 +92,17 @@ export function mediumMetadata(medium: string): {} {
 
 export async function processImage(stream: any, path: [string, string]) {
   const filePath = path.join('/')
-  const thumbnail = `${filePath}.thmb`
+  const thumbnail = `${filePath}.jpg`
   const metadata = await imageResizeConvertJpg(stream, filePath)
   await createThumbnail(filePath, thumbnail)
   return { thumbnail, ...metadata }
 }
 
 export async function processVideo(stream: any, path: [string, string]) {
-  const [folder] = path
   const filePath = path.join('/')
-  const thumbnail = `${filePath}.thmb`
+  const thumbnail = `${filePath}.gif`
   await saveStreamToFile(stream, path)
-  await videoScreenshot(filePath, [folder, thumbnail])
+  await videoScreenshot(filePath, thumbnail)
   const metadata = await mediumMetadata(filePath)
   return { thumbnail, ...metadata }
 }

+ 2 - 1
frontend/pages/admin/training/create.tsx

@@ -13,7 +13,8 @@ const EditTrainingPage = () => {
 
   let content
   if (loading) content = <p>Loading data...</p>
-  if (error) content = <p>Error loading data.</p>
+  else if (error) content = <p>Error loading data.</p>
+  else if (!data) content = <p>No data found.</p>
   else content = <EditTraining training={data.training} />
 
   return <AdminPage>{content}</AdminPage>

+ 18 - 3
frontend/src/gql/index.tsx

@@ -4719,7 +4719,15 @@ export type TrainingTypesQueryVariables = {
 
 export type TrainingTypesQuery = { trainingTypes: Array<Pick<TrainingType, 'id' | 'name' | 'description'>> };
 
-export type FormatsQueryVariables = {};
+export type FormatsQueryVariables = {
+  where?: Maybe<FormatWhereInput>,
+  orderBy?: Maybe<FormatOrderByInput>,
+  skip?: Maybe<Scalars['Int']>,
+  after?: Maybe<Scalars['String']>,
+  before?: Maybe<Scalars['String']>,
+  first?: Maybe<Scalars['Int']>,
+  last?: Maybe<Scalars['Int']>
+};
 
 
 export type FormatsQuery = { formats: Array<Pick<Format, 'id' | 'name' | 'description'>> };
@@ -5539,8 +5547,8 @@ export type TrainingTypesQueryHookResult = ReturnType<typeof useTrainingTypesQue
 export type TrainingTypesLazyQueryHookResult = ReturnType<typeof useTrainingTypesLazyQuery>;
 export type TrainingTypesQueryResult = ApolloReactCommon.QueryResult<TrainingTypesQuery, TrainingTypesQueryVariables>;
 export const FormatsDocument = gql`
-    query formats {
-  formats {
+    query formats($where: FormatWhereInput, $orderBy: FormatOrderByInput, $skip: Int, $after: String, $before: String, $first: Int, $last: Int) {
+  formats(where: $where, orderBy: $orderBy, skip: $skip, after: $after, before: $before, first: $first, last: $last) {
     id
     name
     description
@@ -5560,6 +5568,13 @@ export const FormatsDocument = gql`
  * @example
  * const { data, loading, error } = useFormatsQuery({
  *   variables: {
+ *      where: // value for 'where'
+ *      orderBy: // value for 'orderBy'
+ *      skip: // value for 'skip'
+ *      after: // value for 'after'
+ *      before: // value for 'before'
+ *      first: // value for 'first'
+ *      last: // value for 'last'
  *   },
  * });
  */

+ 4 - 1
frontend/src/training/components/BlockInputs.tsx

@@ -23,7 +23,6 @@ const BlockInputs = (props?: IBlockInputs) => {
       {!value.id.startsWith('++') && (
         <div className='block-info'>
           <div>{value.id}</div>
-          <div>value.createdAt</div>
         </div>
       )}
       <TextInput name={`${name}.title`} label='Title' value={value.title} onChange={onChange} />
@@ -102,6 +101,10 @@ const BlockInputs = (props?: IBlockInputs) => {
       </button>
 
       <style jsx>{`
+        .${className} {
+          display: grid;
+          grid-template-areas: ;
+        }
         .block-info {
           display: flex;
           justify-content: space-between;

+ 24 - 75
frontend/src/training/components/FormatSelector.tsx

@@ -1,91 +1,40 @@
-import { useFormatsQuery, FormatCreateOneInput, Format } from '../../gql'
-import { useEffect, useState } from 'react'
-import CreateFormat from './CreateFormat'
-import { Modal } from '../../modal'
+import { useFormatsQuery, Format, FormatsQuery, FormatsQueryVariables } from '../../gql'
+import Selector from './Selector'
 
 interface IFormatSelector {
   value?: Format
   onChange: GenericEventHandler
   name?: string
   label?: string
+  className?: string
+}
+
+const FormatSelectItem = ({ data }: { data: Format }) => {
+  return (
+    <div>
+      <h3>{data.name}</h3>
+      <p>{data.description}</p>
+    </div>
+  )
 }
 
 const FormatSelector = ({
   value,
   onChange,
-  name = 'format',
-  label = 'Format'
+  name = 'type',
+  label = 'Training type',
+  className = 'training-type',
 }: IFormatSelector) => {
-  const [modalState, setModalState] = useState(false)
-  const formats = useFormatsQuery()
-  const id = value?.id
-
-  useEffect(() => {
-    if (formats.data && formats.data.formats.length > 0 && !id) {
-      onChange({
-        target: {
-          type: 'custom',
-          value: formats.data.formats[0],
-          name
-        }
-      })
-    }
-  }, [formats.data])
-
   return (
-    <>
-      <label>{label}</label>
-      <select
-        id={name}
-        name={name}
-        value={id}
-        onChange={event => {
-          const changeEvent: CustomChangeEvent = {
-            target: {
-              type: 'custom',
-              value: { id: event.target.value },
-              name
-            }
-          }
-          onChange(changeEvent)
-        }}
-      >
-        {formats.loading && 'loading formats...'}
-        {formats.error && 'error loading formats'}
-        {formats.data &&
-          formats.data.formats.map(format => (
-            <option key={format.id} value={format.id}>
-              {format.name}
-            </option>
-          ))}
-      </select>
-      <button
-        type='button'
-        onClick={event => {
-          setModalState(true)
-        }}
-      >
-        Add format
-      </button>
-      <Modal state={[modalState, setModalState]}>
-        <CreateFormat
-          onSuccess={result => {
-            if (result.data) {
-              const changeEvent: CustomChangeEvent = {
-                target: {
-                  type: 'custom',
-                  value: { id: result.data.createFormat.id },
-                  name
-                }
-              }
-              onChange(changeEvent)
-            }
-            setModalState(false)
-          }}
-        />
-      </Modal>
-    </>
+    <Selector<FormatsQuery, FormatsQueryVariables>
+      SelectorItem={FormatSelectItem}
+      name={name}
+      value={value}
+      onChange={onChange}
+      query={useFormatsQuery}
+      searchKeys={['name_contains', 'description_contains']}
+      className={className}
+    />
   )
 }
-
 export default FormatSelector

+ 1 - 2
frontend/src/training/components/TrainingTypeSelector.tsx

@@ -4,8 +4,7 @@ import {
   TrainingTypesQuery,
   TrainingTypesQueryVariables,
 } from '../../gql'
-import { FunctionComponent } from 'react'
-import Selector, { ISelectorItem } from './Selector'
+import Selector from './Selector'
 
 interface ITrainingTypeSelector {
   value?: TrainingType

+ 18 - 2
frontend/src/training/training.graphql

@@ -80,8 +80,24 @@ query trainingTypes(
   }
 }
 
-query formats {
-  formats {
+query formats(
+  $where: FormatWhereInput
+  $orderBy: FormatOrderByInput
+  $skip: Int
+  $after: String
+  $before: String
+  $first: Int
+  $last: Int
+) {
+  formats(
+    where: $where
+    orderBy: $orderBy
+    skip: $skip
+    after: $after
+    before: $before
+    first: $first
+    last: $last
+  ) {
     id
     name
     description