Skip to content

Instantly share code, notes, and snippets.

@masonjeffreys
Created October 27, 2017 20:14
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save masonjeffreys/40f83a0ef4e5391b4691e48242f7c128 to your computer and use it in GitHub Desktop.
Save masonjeffreys/40f83a0ef4e5391b4691e48242f7c128 to your computer and use it in GitHub Desktop.
File Upload Elm 0.18
-- Important parts of file upload through a HTTP Post form in Elm
-- This example reflects an application like a Facebook Post Feed
-- Each post has a comment form at the bottom of the post
-- A comment can be text or attaching a photo
module TopicView exposing (..)
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (..)
import Http
import Post.Model exposing (Post)
import Comment.Model exposing (Comment)
import FileReader as FR exposing (readAsArrayBuffer, NativeFile, parseSelectedFiles)
import Json.Encode as JE
import Json.Decode as JD exposing (field)
import Task exposing (..)
-- Msg TypeComment is sent when user types in the comment <input text_field> box.
-- Msg of FilesSelect is sent on change of file from <input file>. FileReader library is Key!!
-- Msg SubmitComment is fired when user clicks submit button on new comment
type Msg =
| TypeComment Int String
| FilesSelect (FR.FileGroup)
| SubmitComment Int String
| SaveResponse (Result Http.Error String)
| FileData Int (Result FR.Error String)
update : Msg -> Topic -> (Topic, Cmd Msg)
update msg model =
case msg of
TypeComment id currentString ->
let
updateCommentForPost p =
if p.id == id then
{ p | comment = updateComment p.comment currentString}
else
p
in
( { model | posts = List.map updateCommentForPost model.posts}, Cmd.none )
ToggleComments id ->
let
updatePost p =
if p.id == id then
{ p | showing_comments = not p.showing_comments}
else
p
in
( { model | posts = List.map updatePost model.posts}, Cmd.none )
SubmitComment id csrfToken ->
maybeSavePost model id csrfToken
FilesSelect fileGroup ->
let
fileInputId = fileGroup.targetId
file = List.head fileGroup.files
cmdList = []
addFileToComment p =
if ( "file-input-" ++ (toString p.id) ) == fileInputId then
let
(newComment, cmd) = replaceFile p.comment p.id file
in
({ p | comment = newComment }, cmd)
else
(p, Cmd.none)
newPostList = List.map addFileToComment model.posts
(newPosts, cmds) = List.unzip newPostList
in
Debug.log (toString cmds)
{ model | posts = newPosts } ! cmds
SaveResponse (Ok id) ->
({ model | error = Just "saved successfully" }, Cmd.none )
SaveResponse (Err err) ->
let
errMsg =
case err of
Http.BadStatus response ->
response.body
_ ->
"Error Saving!"
in
( { model | error = Just errMsg }, Cmd.none )
FileData postId (Ok str) ->
let
addFileContentsToComment p =
if p.id == postId then
{ p | comment = replaceFileContents p.comment p.id str }
else
p
in
Debug.log str
( { model | posts = List.map addFileContentsToComment model.posts }, Cmd.none )
FileData postId (Err err) ->
Debug.log ("errror ")
Debug.log (FR.prettyPrint err)
{ model | error = Just (FR.prettyPrint err) } ! []
updateComment : Comment -> String -> Comment
updateComment comment str =
{ comment | body = Just str }
replaceFile : Comment -> Int -> Maybe NativeFile -> (Comment, Cmd Msg)
replaceFile comment postId file =
case file of
Just file ->
let
cmdToReadFile = Cmd.none --readFile postId file
in
({ comment | fileToUpload = Just file }, cmdToReadFile)
Nothing ->
({ comment | fileToUpload = Nothing }, Cmd.none)
replaceFileContents : Comment -> Int -> String -> Comment
replaceFileContents comment postId fileContents =
{ comment | fileContents = Just fileContents }
commentEncoder : Comment -> JE.Value
commentEncoder comment =
JE.object[
( "post_comment[body]", JE.string (Maybe.withDefault "" comment.body))
, ("post_comment[document]", JE.string (Maybe.withDefault "" comment.fileContents))
]
maybeSavePost : Topic -> Int -> String -> (Topic, Cmd Msg)
maybeSavePost model postId csrfToken =
let
matchesId post =
post.id == postId
postToSave =
List.filter matchesId model.posts |> List.head
in
case postToSave of
Just p ->
let
( newPost, cmd ) = savePost p csrfToken
in
( model, cmd )
Nothing ->
( model, Cmd.none)
--readFile : Int -> NativeFile -> Cmd Msg
--readFile postId fileValue =
-- FR.readAsArrayBuffer fileValue.blob
-- |> Task.attempt (FileData postId)
savePost : Post -> String -> ( Post, Cmd Msg )
savePost model csrfToken =
--{ model | message = Basics.toString model.selected } ! List.map readTextFile model.selected
--{ p | comment = Comment.Model.initialComment}
let
--event_topic_post_comment_path(@conn, :create, @event.id, @post.topic_id, @post.id)
url =
"http://127.0.0.1:4000/events/1-example-event/topics/1/posts/" ++ ( toString model.id ) ++ "/comments"
headers =
[
Http.header "Authorization" ("Bearer " ++ "xyz"),
Http.header "X-CSRF-Token" csrfToken
-- `Content-Type` - `"application/json"`
-- `Accept` - `"application/json, text/javascript, */*; q=0.01"`
-- `X-Requested-With` - `"XMLHttpRequest"`
]
body =
case model.comment.fileToUpload of
Just file ->
Http.multipartBody [
Http.stringPart "comment[body]" (Maybe.withDefault "" model.comment.body),
FR.filePart "comment[document]" file
]
Nothing ->
Http.multipartBody [
Http.stringPart "comment[body]" (Maybe.withDefault "" model.comment.body)
]
decoder =
field "id" JD.string
request =
post url headers body decoder
cmd =
Http.send SaveResponse request
in
Debug.log (toString request)
( model, cmd )
post : String -> List Http.Header -> Http.Body -> JD.Decoder a -> Http.Request a
post url headers body decoder =
Http.request
{ method = "POST"
, headers = headers
, url = url
, body = body
, expect = Http.expectJson decoder
, timeout = Nothing
, withCredentials = False
}
onPostFileChange : (FR.FileGroup -> msg) -> Attribute msg
onPostFileChange msg =
on "change" (JD.map msg parseSelectedFiles)
commentFormView : Comment -> Int -> String -> Html Msg
commentFormView comment postId csrfToken =
let
fileBtnId =
"file-input-" ++ toString postId
commentText =
case comment.body of
Just bdy ->
bdy
Nothing ->
""
in
div [ class "comment-form-holder"][
input [ class "inputfile", name "post_comment[document]", type_ "file", id fileBtnId, onPostFileChange FilesSelect ][
]
, label [ class "file-input-label", for fileBtnId ][
i [ class "fa fa-camera" ][
]
, text " attach photo"
]
, Html.form [ class "add-runner", onSubmit (SubmitComment postId csrfToken) ][
input [ class "form-control", type_ "text", value commentText, placeholder "Write a comment...", onInput ( TypeComment postId )][
]
, button [ class "btn btn-primary comment-submit", type_ "submit"][
text "post"
]
]
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment