Fix #45: Add a media viewer. (#46)

This commit is contained in:
Nicolas Perriault 2017-04-24 21:21:43 +02:00 committed by GitHub
parent 7e2896dcf0
commit b5fa55f49d
4 changed files with 173 additions and 6 deletions

View File

@ -8,7 +8,7 @@
"debug": "node_modules/.bin/elm-live src/Main.elm --dir=public/ --output=public/app.js --debug", "debug": "node_modules/.bin/elm-live src/Main.elm --dir=public/ --output=public/app.js --debug",
"deploy": "npm run build && node_modules/.bin/gh-pages --dist build/", "deploy": "npm run build && node_modules/.bin/gh-pages --dist build/",
"start": "node_modules/.bin/elm-live src/Main.elm --dir=public/ --output=public/app.js", "start": "node_modules/.bin/elm-live src/Main.elm --dir=public/ --output=public/app.js",
"test": "node_modules/.bin/elm-make src/Main.elm --warn --output /tmp/tooty.html" "test": "node_modules/.bin/elm-make src/Main.elm --warn --output app.js"
}, },
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -130,6 +130,12 @@ body {
.attachment-entry { .attachment-entry {
margin-top: 5px; margin-top: 5px;
transition: 0.3s;
opacity: .9;
}
.attachment-entry:hover {
opacity: 1;
} }
.attachment-entry input { .attachment-entry input {
@ -274,3 +280,78 @@ body {
text-align:center; text-align:center;
padding: 15px; padding: 15px;
} }
/* Viewer */
.viewer {
position: fixed;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0,0,0,0.9);
}
/* Modal Content (Image) */
.viewer-content {
display: block;
margin: auto;
max-width: 80vw;
max-height: 90vh;
animation-name: zoom;
animation-duration: 0.1s;
}
@keyframes zoom {
from { transform: scale(.9) }
to { transform: scale(1) }
}
.viewer > .close {
position: absolute;
top: 15px;
right: 35px;
color: #eee;
font-size: 40px;
font-weight: bold;
transition: 0.3s;
text-shadow: initial;
opacity: 1;
}
.viewer > .close:hover,
.viewer > .close:focus {
color: #fff;
text-decoration: none;
cursor: pointer;
}
.prev, .next {
font-size: 72px;
opacity: .5;
transition: opacity .5s;
text-decoration: none;
margin: 0 10px;
}
.prev:hover,
.next:hover {
opacity: 1;
}
.prev:active, .prev:focus, .prev:hover,
.next:active, .next:focus, .next:hover {
text-decoration: none;
}
/* 100% image width on smaller screens */
@media only screen and (max-width: 700px) {
.viewer-content {
width: 100%;
}
}

View File

@ -25,6 +25,11 @@ type DraftMsg
| ToggleSpoiler Bool | ToggleSpoiler Bool
type ViewerMsg
= CloseViewer
| OpenViewer (List Mastodon.Attachment) Mastodon.Attachment
type type
Msg Msg
{- {-
@ -56,6 +61,7 @@ type
| Unreblog Int | Unreblog Int
| Unreblogged (Result Mastodon.Error Mastodon.Status) | Unreblogged (Result Mastodon.Error Mastodon.Status)
| UserTimeline (Result Mastodon.Error (List Mastodon.Status)) | UserTimeline (Result Mastodon.Error (List Mastodon.Status))
| ViewerEvent ViewerMsg
type alias Draft = type alias Draft =
@ -67,6 +73,12 @@ type alias Draft =
} }
type alias Viewer =
{ attachments : List Mastodon.Attachment
, attachment : Mastodon.Attachment
}
type alias Model = type alias Model =
{ server : String { server : String
, registration : Maybe Mastodon.AppRegistration , registration : Maybe Mastodon.AppRegistration
@ -80,6 +92,7 @@ type alias Model =
, errors : List String , errors : List String
, location : Navigation.Location , location : Navigation.Location
, useGlobalTimeline : Bool , useGlobalTimeline : Bool
, viewer : Maybe Viewer
} }
@ -121,6 +134,7 @@ init flags location =
, errors = [] , errors = []
, location = location , location = location
, useGlobalTimeline = False , useGlobalTimeline = False
, viewer = Nothing
} }
! [ initCommands flags.registration flags.client authCode ] ! [ initCommands flags.registration flags.client authCode ]
@ -308,6 +322,16 @@ updateDraft draftMsg draft =
{ draft | in_reply_to = Nothing } ! [] { draft | in_reply_to = Nothing } ! []
updateViewer : ViewerMsg -> Maybe Viewer -> ( Maybe Viewer, Cmd Msg )
updateViewer viewerMsg viewer =
case viewerMsg of
CloseViewer ->
Nothing ! []
OpenViewer attachments attachment ->
(Just <| Viewer attachments attachment) ! []
update : Msg -> Model -> ( Model, Cmd Msg ) update : Msg -> Model -> ( Model, Cmd Msg )
update msg model = update msg model =
case msg of case msg of
@ -427,6 +451,13 @@ update msg model =
in in
{ model | draft = draft } ! [ commands ] { model | draft = draft } ! [ commands ]
ViewerEvent viewerMsg ->
let
( viewer, commands ) =
updateViewer viewerMsg model.viewer
in
{ model | viewer = viewer } ! [ commands ]
SubmitDraft -> SubmitDraft ->
model model
! case model.client of ! case model.client of

View File

@ -4,8 +4,9 @@ import Dict
import Html exposing (..) import Html exposing (..)
import Html.Attributes exposing (..) import Html.Attributes exposing (..)
import Html.Events exposing (..) import Html.Events exposing (..)
import List.Extra exposing (elemIndex, getAt)
import Mastodon import Mastodon
import Model exposing (Model, Draft, DraftMsg(..), Msg(..)) import Model exposing (Model, Draft, DraftMsg(..), Viewer, ViewerMsg(..), Msg(..))
import ViewHelper import ViewHelper
@ -64,8 +65,8 @@ accountAvatarLink account =
[ img [ class "avatar", src account.avatar ] [] ] [ img [ class "avatar", src account.avatar ] [] ]
attachmentPreview : Maybe Bool -> Mastodon.Attachment -> Html Msg attachmentPreview : Maybe Bool -> List Mastodon.Attachment -> Mastodon.Attachment -> Html Msg
attachmentPreview sensitive ({ url, preview_url } as attachment) = attachmentPreview sensitive attachments ({ url, preview_url } as attachment) =
let let
nsfw = nsfw =
case sensitive of case sensitive of
@ -82,7 +83,8 @@ attachmentPreview sensitive ({ url, preview_url } as attachment) =
a a
[ class "attachment-image" [ class "attachment-image"
, href url , href url
, target "_blank" , ViewHelper.onClickWithPreventAndStop <|
ViewerEvent (OpenViewer attachments attachment)
, style , style
[ ( "background" [ ( "background"
, "url(" ++ preview_url ++ ") center center / cover no-repeat" , "url(" ++ preview_url ++ ") center center / cover no-repeat"
@ -113,7 +115,8 @@ attachmentListView { media_attachments, sensitive } =
text "" text ""
attachments -> attachments ->
ul [ class "attachments" ] <| List.map (attachmentPreview sensitive) attachments ul [ class "attachments" ] <|
List.map (attachmentPreview sensitive attachments) attachments
statusContentView : Mastodon.Status -> Html Msg statusContentView : Mastodon.Status -> Html Msg
@ -595,6 +598,52 @@ authView model =
] ]
viewerView : Viewer -> Html Msg
viewerView { attachments, attachment } =
let
index =
Maybe.withDefault -1 <| elemIndex attachment attachments
( prev, next ) =
( getAt (index - 1) attachments, getAt (index + 1) attachments )
navLink label className target =
case target of
Nothing ->
text ""
Just target ->
a
[ href ""
, class className
, ViewHelper.onClickWithPreventAndStop <|
ViewerEvent (OpenViewer attachments target)
]
[ text label ]
in
div
[ class "viewer"
, tabindex -1
, ViewHelper.onClickWithPreventAndStop <| ViewerEvent CloseViewer
]
[ span [ class "close" ] [ text "×" ]
, navLink "" "prev" prev
, case attachment.type_ of
"image" ->
img [ class "viewer-content", src attachment.url ] []
_ ->
video
[ class "viewer-content"
, preload "auto"
, autoplay True
, loop True
]
[ source [ src attachment.url ] [] ]
, navLink "" "next" next
]
view : Model -> Html Msg view : Model -> Html Msg
view model = view model =
div [ class "container-fluid" ] div [ class "container-fluid" ]
@ -605,4 +654,10 @@ view model =
Nothing -> Nothing ->
authView model authView model
, case model.viewer of
Just viewer ->
viewerView viewer
Nothing ->
text ""
] ]