import generator , { Entity } from 'megalodon' ; import Visibility , { VisibilityType } from '~/src/constants/visibility' ; import TootStatus , { StatusState } from './NewToot/Status' ; import { Module , MutationTree , ActionTree , GetterTree } from 'vuex' ; import { RootState } from '@/store' ; import AxiosLoading from '@/utils/axiosLoading' ; import { NewTootModalOpen , NewTootBlockSubmit , NewTootTootLength , NewTootAttachLength , NewTootMediaDescription , NewTootPollInvalid , NewTootUnknownType , AuthenticationError } from '@/errors/validations' ; import { MyWindow } from '~/src/types/global' ; const win = ( window as any ) as MyWindow ; type MediaDescription = { id : string ; description : string ; } ; type TootForm = { status : string ; spoiler : string ; polls : Array < string > ; pollExpireSeconds : number ; } ; export type NewTootState = { modalOpen : boolean ; initialStatus : string ; initialSpoiler : string ; replyToMessage : Entity . Status | null ; quoteToMessage : Entity . Status | null ; blockSubmit : boolean ; attachedMedias : Array < Entity . Attachment > ; mediaDescriptions : { [ key ] : string | null ; } ; visibility : number ; sensitive : boolean ; attachedMediaCount : number ; pinedHashtag : boolean ; hashtags : Array < Entity . Tag > ; loading : boolean ; } ; type NewTootModule = { Status : StatusState ; } ; export type NewTootModuleState = NewTootModule & NewTootState ; const state = ( ) : NewTootState => ( { modalOpen : false , initialStatus : '' , initialSpoiler : '' , replyToMessage : null , quoteToMessage : null , blockSubmit : false , attachedMedias : [ ] , mediaDescriptions : { } , visibility : Visibility . Public . value , sensitive : false , attachedMediaCount : 0 , pinedHashtag : false , hashtags : [ ] , loading : false } ) ; export const MUTATION_TYPES = { CHANGE_MODAL : 'changeModal' , SET_REPLY_TO : 'setReplyTo' , SET_QUOTE_TO : 'setQuoteTo' , UPDATE_INITIAL_STATUS : 'updateInitialStatus' , UPDATE_INITIAL_SPOILER : 'updateInitialSpoiler' , CHANGE_BLOCK_SUBMIT : 'changeBlockSubmit' , APPEND_ATTACHED_MEDIAS : 'appendAttachedMedias' , CLEAR_ATTACHED_MEDIAS : 'clearAttachedMedias' , REMOVE_MEDIA : 'removeMedia' , UPDATE_MEDIA_DESCRIPTION : 'updateMediaDescription' , CLEAR_MEDIA_DESCRIPTIONS : 'clearMediaDescriptions' , REMOVE_MEDIA_DESCRIPTION : 'removeMediaDescription' , CHANGE_VISIBILITY_VALUE : 'changeVisibilityValue' , CHANGE_SENSITIVE : 'changeSensitive' , UPDATE_MEDIA_COUNT : 'updateMediaCount' , CHANGE_PINED_HASHTAG : 'changePinedHashtag' , UPDATE_HASHTAGS : 'updateHashtags' , CHANGE_LOADING : 'changeLoading' } ; const mutations = { [ MUTATION_TYPES . CHANGE_MODAL ] : ( state , value ) => { state . modalOpen = value ; } , [ MUTATION_TYPES . SET_REPLY_TO ] : ( state , message ) => { state . replyToMessage = message ; } , [ MUTATION_TYPES . SET_QUOTE_TO ] : ( state , message ) => { state . quoteToMessage = message ; } , [ MUTATION_TYPES . UPDATE_INITIAL_STATUS ] : ( state , status ) => { state . initialStatus = status ; } , [ MUTATION_TYPES . UPDATE_INITIAL_SPOILER ] : ( state , cw ) => { state . initialSpoiler = cw ; } , [ MUTATION_TYPES . CHANGE_BLOCK_SUBMIT ] : ( state , value ) => { state . blockSubmit = value ; } , [ MUTATION_TYPES . APPEND_ATTACHED_MEDIAS ] : ( state , media ) => { state . attachedMedias = state . attachedMedias . concat ( [ media ] ) ; } , [ MUTATION_TYPES . CLEAR_ATTACHED_MEDIAS ] : state => { state . attachedMedias = [ ] ; } , [ MUTATION_TYPES . REMOVE_MEDIA ] : ( state , media ) => { state . attachedMedias = state . attachedMedias . filter ( m => m . id !== media . id ) ; } , [ MUTATION_TYPES . UPDATE_MEDIA_DESCRIPTION ] : ( state , value ) => { state . mediaDescriptions [ value . id ] = value . description ; } , [ MUTATION_TYPES . CLEAR_MEDIA_DESCRIPTIONS ] : state => { state . mediaDescriptions = { } ; } , [ MUTATION_TYPES . REMOVE_MEDIA_DESCRIPTION ] : ( state , id ) => { const descriptions = state . mediaDescriptions ; delete descriptions [ id ] ; state . mediaDescriptions = descriptions ; } , param state param value [ MUTATION_TYPES . CHANGE_VISIBILITY_VALUE ] : ( state , value ) => { state . visibility = value ; } , [ MUTATION_TYPES . CHANGE_SENSITIVE ] : ( state , value ) => { state . sensitive = value ; } , [ MUTATION_TYPES . UPDATE_MEDIA_COUNT ] : ( state , count ) => { state . attachedMediaCount = count ; } , [ MUTATION_TYPES . CHANGE_PINED_HASHTAG ] : ( state , value ) => { state . pinedHashtag = value ; } , [ MUTATION_TYPES . UPDATE_HASHTAGS ] : ( state , tags ) => { state . hashtags = tags ; } , [ MUTATION_TYPES . CHANGE_LOADING ] : ( state , value ) => { state . loading = value ; } } ; const actions = { setupLoading : ( { dispatch } ) => { const axiosLoading = new AxiosLoading ( ) ; axiosLoading . on ( 'start' , ( _ ) => { dispatch ( 'startLoading' ) ; } ) ; axiosLoading . on ( 'done' , ( ) => { dispatch ( 'stopLoading' ) ; } ) ; } , startLoading : ( { commit , state } ) => { if ( state . modalOpen && ! state . loading ) { commit ( MUTATION_TYPES . CHANGE_LOADING , true ) ; } } , stopLoading : ( { commit , state } ) => { if ( state . modalOpen && state . loading ) { commit ( MUTATION_TYPES . CHANGE_LOADING , false ) ; } } , updateMedia : async ( { rootState } , mediaDescription ) => { if ( rootState . TimelineSpace . account . accessToken === undefined || rootState . TimelineSpace . account . accessToken === null ) { throw new AuthenticationError ( ) ; } const client = generator ( rootState . TimelineSpace . sns , rootState . TimelineSpace . account . baseURL , rootState . TimelineSpace . account . accessToken , rootState . App . userAgent ) ; const attachments = Object . keys ( mediaDescription ) . map ( async ( id ) => { if ( mediaDescription [ id ] !== null ) { return client . updateMedia ( id , { description : mediaDescription [ id ] } ) ; } else { return Promise . resolve ( { } ) ; } } ) ; return Promise . all ( attachments ) . catch ( err => { console . error ( err ) ; throw err ; } ) ; } , postToot : async ( { state , commit , rootState , dispatch } , params ) : Promise < Entity . Status > => { if ( ! state . modalOpen ) { throw new NewTootModalOpen ( ) ; } if ( params . status . length < 1 || params . status . length > rootState . TimelineSpace . tootMax ) { throw new NewTootTootLength ( ) ; } const visibilityKey = Object . keys ( Visibility ) . find ( key => { return Visibility [ key ] . value === state . visibility ; } ) ; let specifiedVisibility = Visibility . Public . key ; if ( visibilityKey !== undefined ) { specifiedVisibility = Visibility [ visibilityKey ] . key ; } let form = { visibility : specifiedVisibility , sensitive : state . sensitive , spoiler_text : params . spoiler } ; if ( state . replyToMessage !== null ) { form = Object . assign ( form , { in_reply_to_id : state . replyToMessage . id } ) ; } if ( state . quoteToMessage !== null ) { form = Object . assign ( form , { quote_id : state . quoteToMessage . id } ) ; } if ( params . polls . length > 1 ) { params . polls . forEach ( poll => { if ( poll . length < 1 ) { throw new NewTootPollInvalid ( ) ; } } ) ; form = Object . assign ( form , { poll : { expires_in : params . pollExpireSeconds , multiple : false , options : params . polls } } ) ; } if ( rootState . TimelineSpace . account . accessToken === undefined || rootState . TimelineSpace . account . accessToken === null ) { throw new AuthenticationError ( ) ; } if ( state . blockSubmit ) { throw new NewTootBlockSubmit ( ) ; } if ( state . attachedMedias . length > 0 ) { if ( state . attachedMedias . length > 4 ) { throw new NewTootAttachLength ( ) ; } form = Object . assign ( form , { media_ids : state . attachedMedias . map ( m => { return m . id ; } ) } ) ; await dispatch ( 'updateMedia' , state . mediaDescriptions ) . catch ( _ => { throw new NewTootMediaDescription ( ) ; } ) ; } commit ( MUTATION_TYPES . CHANGE_BLOCK_SUBMIT , true ) ; const client = generator ( rootState . TimelineSpace . sns , rootState . TimelineSpace . account . baseURL , rootState . TimelineSpace . account . accessToken , rootState . App . userAgent ) ; return client . postStatus ( params . status , form ) . then ( res => { win . ipcRenderer . send ( 'toot-action-sound' ) ; return res . data ; } ) . finally ( ( ) => { commit ( MUTATION_TYPES . CHANGE_BLOCK_SUBMIT , false ) ; } ) ; } , openReply : ( { commit , rootState } , message ) => { commit ( MUTATION_TYPES . SET_REPLY_TO , message ) ; const mentionAccounts = [ message . account . acct ] . concat ( message . mentions . map ( a => a . acct ) ) . filter ( ( a , i , self ) => self . indexOf ( a ) === i ) . filter ( a => a !== rootState . TimelineSpace . account . username ) ; commit ( MUTATION_TYPES . UPDATE_INITIAL_STATUS , `${ mentionAccounts . map ( m => `@${ m }` ) . join ( ' ' ) } ` ) ; commit ( MUTATION_TYPES . UPDATE_INITIAL_SPOILER , message . spoiler_text ) ; commit ( MUTATION_TYPES . CHANGE_MODAL , true ) ; let value = Visibility . Public . value ; Object . keys ( Visibility ) . forEach ( key => { const target = Visibility [ key ] ; if ( target . key === message . visibility ) { value = target . value ; } } ) ; commit ( MUTATION_TYPES . CHANGE_VISIBILITY_VALUE , value ) ; } , openQuote : ( { commit } , message ) => { commit ( MUTATION_TYPES . SET_QUOTE_TO , message ) ; commit ( MUTATION_TYPES . CHANGE_MODAL , true ) ; } , openModal : ( { dispatch , commit , state } ) => { if ( ! state . replyToMessage && state . pinedHashtag ) { commit ( MUTATION_TYPES . UPDATE_INITIAL_STATUS , state . hashtags . map ( t => `#${ t . name }` ) . join ( ' ' ) ) ; } commit ( MUTATION_TYPES . CHANGE_MODAL , true ) ; dispatch ( 'fetchVisibility' ) ; } , closeModal : ( { commit } ) => { commit ( MUTATION_TYPES . CHANGE_MODAL , false ) ; commit ( MUTATION_TYPES . UPDATE_INITIAL_STATUS , '' ) ; commit ( MUTATION_TYPES . UPDATE_INITIAL_SPOILER , '' ) ; commit ( MUTATION_TYPES . SET_REPLY_TO , null ) ; commit ( MUTATION_TYPES . SET_QUOTE_TO , null ) ; commit ( MUTATION_TYPES . CHANGE_BLOCK_SUBMIT , false ) ; commit ( MUTATION_TYPES . CLEAR_ATTACHED_MEDIAS ) ; commit ( MUTATION_TYPES . CLEAR_MEDIA_DESCRIPTIONS ) ; commit ( MUTATION_TYPES . CHANGE_SENSITIVE , false ) ; commit ( MUTATION_TYPES . CHANGE_VISIBILITY_VALUE , Visibility . Public . value ) ; } , uploadImage : async ( { commit , state , rootState } , image ) => { if ( state . attachedMedias . length > 3 ) { throw new NewTootAttachLength ( ) ; } commit ( MUTATION_TYPES . CHANGE_BLOCK_SUBMIT , true ) ; if ( rootState . TimelineSpace . account . accessToken === undefined || rootState . TimelineSpace . account . accessToken === null ) { throw new AuthenticationError ( ) ; } const client = generator ( rootState . TimelineSpace . sns , rootState . TimelineSpace . account . baseURL , rootState . TimelineSpace . account . accessToken , rootState . App . userAgent ) ; return client . uploadMedia ( image ) . then ( res => { commit ( MUTATION_TYPES . CHANGE_BLOCK_SUBMIT , false ) ; if ( res . data . type === 'unknown' ) throw new NewTootUnknownType ( ) ; commit ( MUTATION_TYPES . APPEND_ATTACHED_MEDIAS , res . data ) ; return res . data ; } ) . catch ( err => { commit ( MUTATION_TYPES . CHANGE_BLOCK_SUBMIT , false ) ; console . error ( err ) ; throw err ; } ) ; } , incrementMediaCount : ( { commit , state } ) => { commit ( MUTATION_TYPES . UPDATE_MEDIA_COUNT , state . attachedMediaCount + 1 ) ; } , decrementMediaCount : ( { commit , state } ) => { commit ( MUTATION_TYPES . UPDATE_MEDIA_COUNT , state . attachedMediaCount - 1 ) ; } , resetMediaCount : ( { commit } ) => { commit ( MUTATION_TYPES . UPDATE_MEDIA_COUNT , 0 ) ; } , removeMedia : ( { commit , dispatch } , media ) => { commit ( MUTATION_TYPES . REMOVE_MEDIA , media ) ; commit ( MUTATION_TYPES . REMOVE_MEDIA_DESCRIPTION , media . id ) ; dispatch ( 'decrementMediaCount' ) ; } , updateHashtags : ( { commit , state } , tags ) => { if ( state . pinedHashtag && tags . length > 0 ) { commit ( MUTATION_TYPES . UPDATE_HASHTAGS , tags ) ; } } , fetchVisibility : async ( { commit , rootState } ) => { const client = generator ( rootState . TimelineSpace . sns , rootState . TimelineSpace . account . baseURL , rootState . TimelineSpace . account . accessToken , rootState . App . userAgent ) ; const res = await client . verifyAccountCredentials ( ) ; const visibility = ( Object . values ( Visibility ) as Array < VisibilityType > ) . find ( v => { return res . data . source !== undefined && v . key === res . data . source . privacy ; } ) ; if ( visibility === undefined ) { throw new Error ( 'Visibility value is invalid' ) ; } commit ( MUTATION_TYPES . CHANGE_VISIBILITY_VALUE , visibility . value ) ; return res . data ; } } ; const getters = { hashtagInserting : state => { return ! state . replyToMessage && state . pinedHashtag ; } } ; const NewToot = { namespaced : true , modules : { Status : TootStatus } , state : state , mutations : mutations , getters : getters , actions : actions } ; export default NewToot ;