2017-05-07 08:31:51 -04:00
|
|
|
module Update.Draft
|
|
|
|
exposing
|
|
|
|
( empty
|
|
|
|
, showAutoMenu
|
|
|
|
, update
|
|
|
|
)
|
|
|
|
|
|
|
|
import Autocomplete
|
|
|
|
import Command
|
2017-05-11 04:55:15 -04:00
|
|
|
import Json.Decode as Decode
|
|
|
|
import Mastodon.Decoder exposing (attachmentDecoder)
|
2017-05-07 08:31:51 -04:00
|
|
|
import Mastodon.Helper
|
|
|
|
import Mastodon.Model exposing (..)
|
|
|
|
import String.Extra
|
2017-05-11 04:55:15 -04:00
|
|
|
import Update.Error exposing (addErrorNotification)
|
2017-05-07 08:31:51 -04:00
|
|
|
import Types exposing (..)
|
|
|
|
import Util
|
|
|
|
|
|
|
|
|
|
|
|
autocompleteUpdateConfig : Autocomplete.UpdateConfig Msg Account
|
|
|
|
autocompleteUpdateConfig =
|
|
|
|
Autocomplete.updateConfig
|
2017-11-29 08:06:08 -05:00
|
|
|
{ toId = .id
|
2017-05-07 08:31:51 -04:00
|
|
|
, onKeyDown =
|
|
|
|
\code maybeId ->
|
|
|
|
if code == 38 || code == 40 then
|
|
|
|
Nothing
|
|
|
|
else if code == 13 then
|
|
|
|
Maybe.map (DraftEvent << SelectAccount) maybeId
|
|
|
|
else
|
|
|
|
Just <| (DraftEvent << ResetAutocomplete) False
|
|
|
|
, onTooLow = Just <| (DraftEvent << ResetAutocomplete) True
|
|
|
|
, onTooHigh = Just <| (DraftEvent << ResetAutocomplete) False
|
|
|
|
, onMouseEnter = \_ -> Nothing
|
|
|
|
, onMouseLeave = \_ -> Nothing
|
|
|
|
, onMouseClick = \id -> Just <| (DraftEvent << SelectAccount) id
|
|
|
|
, separateSelections = False
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
empty : Draft
|
|
|
|
empty =
|
|
|
|
{ status = ""
|
|
|
|
, inReplyTo = Nothing
|
|
|
|
, spoilerText = Nothing
|
|
|
|
, sensitive = False
|
|
|
|
, visibility = "public"
|
2017-05-11 04:55:15 -04:00
|
|
|
, attachments = []
|
2017-05-11 06:23:10 -04:00
|
|
|
, mediaUploading = False
|
2017-05-07 08:31:51 -04:00
|
|
|
, statusLength = 0
|
|
|
|
, autoState = Autocomplete.empty
|
|
|
|
, autoAtPosition = Nothing
|
|
|
|
, autoQuery = ""
|
|
|
|
, autoCursorPosition = 0
|
|
|
|
, autoMaxResults = 4
|
|
|
|
, autoAccounts = []
|
|
|
|
, showAutoMenu = False
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
showAutoMenu : List Account -> Maybe Int -> String -> Bool
|
|
|
|
showAutoMenu accounts atPosition query =
|
|
|
|
case ( List.isEmpty accounts, atPosition, query ) of
|
|
|
|
( _, Nothing, _ ) ->
|
|
|
|
False
|
|
|
|
|
|
|
|
( True, _, _ ) ->
|
|
|
|
False
|
|
|
|
|
|
|
|
( _, _, "" ) ->
|
|
|
|
False
|
|
|
|
|
|
|
|
( False, Just _, _ ) ->
|
|
|
|
True
|
|
|
|
|
|
|
|
|
|
|
|
update : DraftMsg -> Account -> Model -> ( Model, Cmd Msg )
|
2017-05-11 04:55:15 -04:00
|
|
|
update draftMsg currentUser ({ draft } as model) =
|
|
|
|
case draftMsg of
|
|
|
|
ClearDraft ->
|
2021-01-10 22:12:21 -05:00
|
|
|
let
|
|
|
|
newDraft =
|
|
|
|
{ empty
|
|
|
|
| visibility = currentUser.source.privacy
|
|
|
|
, sensitive = currentUser.source.sensitive
|
|
|
|
}
|
|
|
|
in
|
|
|
|
{ model | draft = newDraft }
|
|
|
|
! [ Command.updateDomStatus newDraft.status ]
|
2017-05-11 04:55:15 -04:00
|
|
|
|
|
|
|
ToggleSpoiler enabled ->
|
|
|
|
let
|
|
|
|
newDraft =
|
|
|
|
{ draft
|
|
|
|
| spoilerText =
|
|
|
|
if enabled then
|
|
|
|
Just ""
|
|
|
|
else
|
2017-05-07 08:31:51 -04:00
|
|
|
Nothing
|
2017-05-11 04:55:15 -04:00
|
|
|
}
|
|
|
|
in
|
|
|
|
{ model | draft = newDraft } ! []
|
2017-05-07 08:31:51 -04:00
|
|
|
|
2017-05-11 04:55:15 -04:00
|
|
|
UpdateSensitive sensitive ->
|
|
|
|
{ model | draft = { draft | sensitive = sensitive } } ! []
|
2017-05-07 08:31:51 -04:00
|
|
|
|
2017-05-11 04:55:15 -04:00
|
|
|
UpdateSpoiler spoilerText ->
|
|
|
|
{ model | draft = { draft | spoilerText = Just spoilerText } } ! []
|
2017-05-07 08:31:51 -04:00
|
|
|
|
2017-05-11 04:55:15 -04:00
|
|
|
UpdateVisibility visibility ->
|
|
|
|
{ model | draft = { draft | visibility = visibility } } ! []
|
2017-05-07 08:31:51 -04:00
|
|
|
|
2017-05-11 04:55:15 -04:00
|
|
|
UpdateReplyTo status ->
|
|
|
|
let
|
|
|
|
newStatus =
|
|
|
|
Mastodon.Helper.getReplyPrefix currentUser status
|
|
|
|
in
|
|
|
|
{ model
|
|
|
|
| draft =
|
2017-05-07 08:31:51 -04:00
|
|
|
{ draft
|
2017-05-11 04:55:15 -04:00
|
|
|
| inReplyTo = Just status
|
|
|
|
, status = newStatus
|
|
|
|
, sensitive = Maybe.withDefault False status.sensitive
|
|
|
|
, spoilerText =
|
|
|
|
if status.spoiler_text == "" then
|
|
|
|
Nothing
|
|
|
|
else
|
|
|
|
Just status.spoiler_text
|
|
|
|
, visibility = status.visibility
|
2017-05-07 08:31:51 -04:00
|
|
|
}
|
2017-05-11 04:55:15 -04:00
|
|
|
}
|
|
|
|
! [ Command.focusId "status"
|
|
|
|
, Command.updateDomStatus newStatus
|
|
|
|
]
|
2017-05-07 08:31:51 -04:00
|
|
|
|
2017-05-11 04:55:15 -04:00
|
|
|
UpdateInputInformation { status, selectionStart } ->
|
|
|
|
let
|
|
|
|
stringToPos =
|
|
|
|
String.slice 0 selectionStart status
|
2017-05-07 08:31:51 -04:00
|
|
|
|
2017-05-11 04:55:15 -04:00
|
|
|
atPosition =
|
|
|
|
case (String.right 1 stringToPos) of
|
|
|
|
"@" ->
|
|
|
|
Just selectionStart
|
|
|
|
|
|
|
|
" " ->
|
|
|
|
Nothing
|
2017-05-07 08:31:51 -04:00
|
|
|
|
|
|
|
_ ->
|
2017-05-11 04:55:15 -04:00
|
|
|
model.draft.autoAtPosition
|
|
|
|
|
|
|
|
query =
|
|
|
|
case atPosition of
|
|
|
|
Just position ->
|
|
|
|
String.slice position (String.length stringToPos) stringToPos
|
|
|
|
|
|
|
|
Nothing ->
|
|
|
|
""
|
|
|
|
|
|
|
|
newDraft =
|
|
|
|
{ draft
|
|
|
|
| status = status
|
|
|
|
, statusLength = String.length status
|
|
|
|
, autoCursorPosition = selectionStart
|
|
|
|
, autoAtPosition = atPosition
|
|
|
|
, autoQuery = query
|
|
|
|
, showAutoMenu =
|
|
|
|
showAutoMenu
|
|
|
|
draft.autoAccounts
|
|
|
|
draft.autoAtPosition
|
|
|
|
draft.autoQuery
|
|
|
|
}
|
|
|
|
in
|
|
|
|
{ model | draft = newDraft }
|
|
|
|
! if query /= "" && atPosition /= Nothing then
|
|
|
|
[ Command.searchAccounts (List.head model.clients) query model.draft.autoMaxResults False ]
|
|
|
|
else
|
|
|
|
[]
|
|
|
|
|
|
|
|
SelectAccount id ->
|
|
|
|
let
|
|
|
|
account =
|
2017-11-29 08:06:08 -05:00
|
|
|
List.filter (\account -> account.id == id) draft.autoAccounts
|
2017-05-11 04:55:15 -04:00
|
|
|
|> List.head
|
|
|
|
|
|
|
|
stringToAtPos =
|
|
|
|
case draft.autoAtPosition of
|
|
|
|
Just atPosition ->
|
|
|
|
String.slice 0 atPosition draft.status
|
2017-05-07 08:31:51 -04:00
|
|
|
|
2017-05-11 04:55:15 -04:00
|
|
|
_ ->
|
|
|
|
""
|
|
|
|
|
|
|
|
stringToPos =
|
|
|
|
String.slice 0 draft.autoCursorPosition draft.status
|
|
|
|
|
|
|
|
newStatus =
|
|
|
|
case draft.autoAtPosition of
|
|
|
|
Just atPosition ->
|
|
|
|
String.Extra.replaceSlice
|
|
|
|
(case account of
|
|
|
|
Just a ->
|
|
|
|
a.acct ++ " "
|
|
|
|
|
|
|
|
Nothing ->
|
|
|
|
""
|
|
|
|
)
|
|
|
|
atPosition
|
|
|
|
((String.length draft.autoQuery) + atPosition)
|
|
|
|
draft.status
|
2017-05-07 08:31:51 -04:00
|
|
|
|
2017-05-11 04:55:15 -04:00
|
|
|
_ ->
|
|
|
|
""
|
|
|
|
|
|
|
|
newDraft =
|
|
|
|
{ draft
|
|
|
|
| status = newStatus
|
|
|
|
, autoAtPosition = Nothing
|
|
|
|
, autoQuery = ""
|
|
|
|
, autoState = Autocomplete.empty
|
|
|
|
, autoAccounts = []
|
|
|
|
, showAutoMenu = False
|
|
|
|
}
|
|
|
|
in
|
|
|
|
{ model | draft = newDraft }
|
|
|
|
-- As we are using defaultValue, we need to update the textarea
|
|
|
|
-- using a port.
|
|
|
|
! [ Command.updateDomStatus newStatus ]
|
|
|
|
|
|
|
|
SetAutoState autoMsg ->
|
|
|
|
let
|
|
|
|
( newState, maybeMsg ) =
|
|
|
|
Autocomplete.update
|
|
|
|
autocompleteUpdateConfig
|
|
|
|
autoMsg
|
|
|
|
draft.autoMaxResults
|
|
|
|
draft.autoState
|
|
|
|
(Util.acceptableAccounts draft.autoQuery draft.autoAccounts)
|
|
|
|
|
|
|
|
newModel =
|
|
|
|
{ model | draft = { draft | autoState = newState } }
|
|
|
|
in
|
|
|
|
case maybeMsg of
|
|
|
|
Just (DraftEvent updateMsg) ->
|
|
|
|
update updateMsg currentUser newModel
|
|
|
|
|
|
|
|
_ ->
|
|
|
|
newModel ! []
|
|
|
|
|
|
|
|
CloseAutocomplete ->
|
|
|
|
let
|
|
|
|
newDraft =
|
|
|
|
{ draft
|
|
|
|
| showAutoMenu = False
|
|
|
|
, autoState = Autocomplete.reset autocompleteUpdateConfig draft.autoState
|
|
|
|
}
|
|
|
|
in
|
|
|
|
{ model | draft = newDraft } ! []
|
|
|
|
|
|
|
|
ResetAutocomplete toTop ->
|
|
|
|
let
|
|
|
|
newDraft =
|
|
|
|
{ draft
|
|
|
|
| autoState =
|
|
|
|
if toTop then
|
|
|
|
Autocomplete.resetToFirstItem
|
|
|
|
autocompleteUpdateConfig
|
|
|
|
(Util.acceptableAccounts draft.autoQuery draft.autoAccounts)
|
|
|
|
draft.autoMaxResults
|
|
|
|
draft.autoState
|
|
|
|
else
|
|
|
|
Autocomplete.resetToLastItem
|
|
|
|
autocompleteUpdateConfig
|
|
|
|
(Util.acceptableAccounts draft.autoQuery draft.autoAccounts)
|
|
|
|
draft.autoMaxResults
|
|
|
|
draft.autoState
|
|
|
|
}
|
|
|
|
in
|
|
|
|
{ model | draft = newDraft } ! []
|
|
|
|
|
|
|
|
RemoveMedia id ->
|
|
|
|
let
|
|
|
|
newDraft =
|
|
|
|
{ draft | attachments = List.filter (\a -> a.id /= id) draft.attachments }
|
|
|
|
in
|
|
|
|
{ model | draft = newDraft } ! []
|
|
|
|
|
|
|
|
UploadMedia id ->
|
2017-05-11 06:23:10 -04:00
|
|
|
{ model | draft = { draft | mediaUploading = True } }
|
|
|
|
! [ Command.uploadMedia (List.head model.clients) id ]
|
2017-05-11 04:55:15 -04:00
|
|
|
|
|
|
|
UploadError error ->
|
2017-05-11 06:23:10 -04:00
|
|
|
{ model
|
|
|
|
| draft = { draft | mediaUploading = False }
|
|
|
|
, errors = addErrorNotification error model
|
|
|
|
}
|
|
|
|
! []
|
2017-05-11 04:55:15 -04:00
|
|
|
|
|
|
|
UploadResult encoded ->
|
|
|
|
if encoded == "" then
|
|
|
|
-- user has likely pressed "Cancel" in the file input dialog
|
|
|
|
model ! []
|
|
|
|
else
|
2017-05-07 08:31:51 -04:00
|
|
|
let
|
2017-05-11 04:55:15 -04:00
|
|
|
decodedAttachment =
|
|
|
|
Decode.decodeString attachmentDecoder encoded
|
2017-05-07 08:31:51 -04:00
|
|
|
in
|
2017-05-11 04:55:15 -04:00
|
|
|
case decodedAttachment of
|
|
|
|
Ok attachment ->
|
|
|
|
{ model
|
|
|
|
| draft =
|
|
|
|
{ draft
|
2017-05-11 06:23:10 -04:00
|
|
|
| mediaUploading = False
|
|
|
|
, attachments = List.append draft.attachments [ attachment ]
|
2017-05-11 04:55:15 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
! []
|
|
|
|
|
|
|
|
Err error ->
|
2017-05-11 06:23:10 -04:00
|
|
|
{ model
|
|
|
|
| draft = { draft | mediaUploading = False }
|
|
|
|
, errors = addErrorNotification error model
|
|
|
|
}
|
|
|
|
! []
|