import Debug from 'debug' ; import { ComponentType } from 'react' ; import { Handle } from 'hypermerge' ; import { HypermergeUrl , createDocumentLink } from './ShareLink' ; const log = Debug ( 'pushpin:content-types' ) ; type Component = ComponentType < any > ; export type Context = 'root' | 'workspace' | 'list' | 'board' | 'thread' | 'title-bar' ; type Contexts = { [ K in Context ] ? : Component ; } ; interface ContentType { type : string ; name : string ; icon : string ; unlisted ? : boolean ; resizable ? : boolean ; contexts : Contexts ; create ? : ( typeAttrs : any , handle : Handle < any > , callback : ( ) => void ) => void ; createFromFile ? : ( file : File , handle : Handle < any > , callback : ( ) => void ) => void ; supportsMimeType ? : ( type : string ) => boolean ; } const registry = { } ; const defaultRegistry = { } ; function register ( contentType ) { const { type } = contentType ; const entry = { unlisted : false , resiable : true , ... contentType } ; log ( 'register' , entry ) ; if ( registry [ type ] ) { log ( `Replacing '${ type }' content type.` ) ; } registry [ type ] = entry ; } function registerDefault ( contentType ) { const { component , context } = contentType ; defaultRegistry [ context ] = component ; } export interface LookupQuery { type : string ; context : Context ; } export interface LookupResult { type : string ; name : string ; icon : string ; unlisted : boolean ; resizable : boolean ; component : Component ; } function lookup ( { type , context } ) { const entry = registry [ type ] ; const component = ( entry && entry . contexts [ context ] ) || defaultRegistry [ context ] ; if ( ! component ) { return null ; } const { name = 'Unknown' , icon = 'question' , unlisted = false , resizable = true } = entry || { } ; return { type , name , icon , component , unlisted , resizable } ; } function mimeTypeToContentType ( mimeType ) { if ( ! mimeType ) { return 'file' ; } const types = Object . values ( registry ) ; const supportingType = types . find ( ( type ) => type . supportsMimeType && type . supportsMimeType ( mimeType ) ) ; if ( ! supportingType ) { return 'file' ; } return supportingType . type ; } function createFromFile ( file , callback ) { const type = ( ( mimeType ) => { if ( mimeType && mimeType . match ( 'text/' ) ) { return 'text' ; } return 'file' ; } ) ( file . type ) ; const entry = registry [ type ] ; if ( ! entry ) { return ; } if ( ! entry . createFromFile ) { throw Error ( `The ${ type } content type cannot be created from a file directly.` ) ; } const url = window . repo . create ( ) as HypermergeUrl ; const handle = window . repo . open ( url ) ; entry . createFromFile ( file , handle , ( ) => { callback ( createDocumentLink ( type , url ) ) ; } ) ; } function create ( type , attrs = { } , callback ) { const entry = registry [ type ] ; if ( ! entry ) { return ; } const url = window . repo . create ( ) as HypermergeUrl ; const handle = window . repo . open ( url ) ; if ( ! entry . create ) { throw Error ( `The ${ type } content type cannot be created directly.` ) ; } entry . create ( attrs , handle , ( ) => { callback ( createDocumentLink ( type , url ) ) ; } ) ; } export interface ListQuery { context : Context ; withUnlisted ? : boolean ; } function list ( { context , withUnlisted = false } ) { const allTypes = Object . keys ( registry ) . map ( ( type ) => lookup ( { type , context } ) ) . filter ( ( ct ) => ct ) as LookupResult [ ] ; if ( withUnlisted ) { return allTypes ; } return allTypes . filter ( ( ct ) => ct && ! ct . unlisted ) ; } export default { register , registerDefault , lookup , list , createFromFile , create , mimeTypeToContentType } ;