From 1f4866062e821279d5f6a8649c3e0003627891ec Mon Sep 17 00:00:00 2001 From: Vincent Jousse Date: Fri, 21 Jun 2024 10:48:36 +0200 Subject: [PATCH] feat: better account detail view --- public/style.css | 108 +++++++++++++++++++++++------ src/Update/Mastodon.elm | 1 - src/View/Account.elm | 127 ++++++++++++++++++++++++----------- src/View/AccountSelector.elm | 2 +- src/View/Blocks.elm | 2 +- src/View/Common.elm | 45 ++++++++++--- src/View/Draft.elm | 2 +- src/View/Mutes.elm | 2 +- src/View/Notification.elm | 4 +- src/View/Search.elm | 2 +- src/View/Status.elm | 8 +-- 11 files changed, 222 insertions(+), 81 deletions(-) diff --git a/public/style.css b/public/style.css index cae9bc96..088fb6e0 100644 --- a/public/style.css +++ b/public/style.css @@ -677,24 +677,8 @@ input.form-control[type="file"] { } /* Account rules */ -.account-detail { - position: relative; - text-align: center; -} - -.account-detail .opacity-layer { - background: rgba(49, 53, 67, 0.9); -} - -.account-detail img { - border-radius: 90px; - padding: 20px; - display: block; - margin: 0 auto 0; -} .account-detail .btn { - position: absolute; top: 10px; right: 10px; width: 42px; @@ -712,17 +696,36 @@ input.form-control[type="file"] { .account-detail .account-display-name { display: block; - font-size: 130%; + font-size: 140%; font-weight: bold; + color: #fff; + + display: flex; } -.account-detail .account-username { - display: block; +.account-detail .account-display-name .relationship { + flex-grow: 1; + text-align: right; + padding-right: 20px; } .account-detail .account-note { display: block; - padding: 5px 15px 15px 15px; + padding: 5px 20px 5px 0px; + color: #fff; +} + +.account-detail .account-fields { + padding: 10px 10px 10px 10px; + color: #fff; + background-color: #62686d; + border-radius: 5px; + margin-right: 20px; + margin-bottom: 20px; +} + +.account-detail .account-fields .check-mark { + margin-left: 10px; } .account-infos { @@ -731,7 +734,8 @@ input.form-control[type="file"] { } .account-infos.row { - margin-right: 0; + margin: 0; + padding: 0; } .account-infos a { @@ -744,6 +748,68 @@ input.form-control[type="file"] { color: #fff; } +.account-infos .count { + font-weight: 700; +} + +.account-infos .btn { + border-radius: 0; + border-bottom: none; +} + +.account-detail img.avatar-detailed { + display: block; + width: 90px; + border-radius: 5px; + border: 2px solid #d9e1e8; + padding: 0; +} + +.account-detail .account-header-image { + position: relative; +} + +.account-detail .account-header-image img { + border-radius: 0; + margin: 0; + padding: 0; + height: 200px; + width: 100%; + object-fit: cover; +} + +.account-detail .account-header-image.missing-header { + height: 80px; + background: rgba(49, 53, 67, 0.9); +} + +.account-detail .account-header-bar { + margin-top: -40px; + padding: 0 20px; + position: relative; + display: flex; +} + +.account-detail .account-header-actions { + display: flex; + flex-grow: 1; + justify-content: flex-end; + align-items: flex-end; + gap: 5px; +} + +.account-detail .account-header-content { + padding-left: 20px; + padding-top: 20px; + position: relative; +} + +.account-detail .account-username .joined-date { + color: #a0a0a0; + margin-left: 10px; + font-style: italic; +} + .followed-by { margin-left: 5px; } diff --git a/src/Update/Mastodon.elm b/src/Update/Mastodon.elm index f8135610..a38a1ead 100644 --- a/src/Update/Mastodon.elm +++ b/src/Update/Mastodon.elm @@ -3,7 +3,6 @@ module Update.Mastodon exposing (update) import Browser.Navigation as Navigation import Command import InfiniteScroll -import Mastodon.ApiUrl exposing (customEmojis) import Mastodon.Helper exposing (extractStatusId) import Mastodon.Model exposing (..) import Types exposing (..) diff --git a/src/View/Account.elm b/src/View/Account.elm index 7ca9bd00..305d72af 100644 --- a/src/View/Account.elm +++ b/src/View/Account.elm @@ -60,7 +60,7 @@ followButton currentUser relationship account = followView : CurrentUser -> Maybe Relationship -> Account -> Html Msg followView currentUser relationship account = div [ class "follow-entry" ] - [ Common.accountAvatarLink False account + [ Common.accountAvatarLink False Nothing account , div [ class "userinfo" ] [ strong [] [ a @@ -207,18 +207,15 @@ counterLink href_ label count active = a [ href href_ , class <| - "col-md-4" + "btn col-md-4" ++ (if active then - " active" + " btn-default" else "" ) ] - [ text label - , br [] [] - , text <| String.fromInt count - ] + [ span [ class "count" ] [ text <| String.fromInt count ], text " ", text label ] counterLinks : CurrentAccountView -> Account -> Html Msg @@ -259,42 +256,94 @@ accountView subView currentUser accountInfo = , div [ id "account", class "timeline" ] [ div [ class "account-detail" - , style "background-image" ("url('" ++ account.header ++ "')") + + --, style "background-image" ("url('" ++ account.header ++ "')") ] - [ div [ class "opacity-layer" ] - [ followButton currentUser accountInfo.relationship account - , muteButton currentUser accountInfo.relationship account - , blockButton currentUser accountInfo.relationship account - , Common.accountAvatarLink True account - , span [ class "account-display-name" ] (getDisplayNameForAccount account) - , span [ class "account-username" ] + [ div + [ class + ("account-header-image" + -- It looks like mastodon always returns a header image even if none was setup + -- and it's called missing.png + ++ (if String.contains "missing.png" account.header then + " missing-header" + + else + "" + ) + ) + ] + [ img [ src account.header ] [] ] + , div [ class "account-header-bar" ] + [ Common.accountAvatarLink True (Just [ "avatar-detailed" ]) account + , div [ class "account-header-actions" ] + [ followButton currentUser accountInfo.relationship account + , muteButton currentUser accountInfo.relationship account + , blockButton currentUser accountInfo.relationship account + ] + ] + , div [ class "account-header-content" ] + [ div [ class "account-display-name" ] + [ div [] (getDisplayNameForAccount account) + , div [ class "relationship" ] + [ case accountInfo.relationship of + Just relationship -> + span [] + [ if relationship.followed_by && relationship.following then + span [ class "badge followed-by" ] [ text "Following each other" ] + + else if relationship.followed_by then + span [ class "badge followed-by" ] [ text "Follows you" ] + + else if relationship.following then + span [ class "badge followed-by" ] [ text "Following" ] + + else + text "" + , text " " + , if relationship.muting then + span [ class "badge muting" ] [ text "Muted" ] + + else + text "" + , text " " + , if relationship.blocking then + span [ class "badge blocking" ] [ text "Blocked" ] + + else + text "" + ] + + Nothing -> + text "" + ] + ] + , div [ class "account-username" ] [ Common.accountLink True account - , case accountInfo.relationship of - Just relationship -> - span [] - [ if relationship.followed_by then - span [ class "badge followed-by" ] [ text "Follows you" ] - - else - text "" - , text " " - , if relationship.muting then - span [ class "badge muting" ] [ text "Muted" ] - - else - text "" - , text " " - , if relationship.blocking then - span [ class "badge blocking" ] [ text "Blocked" ] - - else - text "" - ] - - Nothing -> - text "" + , span [ class "joined-date" ] [ text "joined on ", text <| Common.formatDate account.created_at ] ] , span [ class "account-note" ] (formatContentWithEmojis account.note [] account.emojis) + , if List.isEmpty account.fields then + text "" + + else + div [ class "account-fields" ] + (List.map + (\field -> + div [] + (formatContentWithEmojis (String.toUpper field.name) [] account.emojis + ++ text " | " + :: formatContentWithEmojis field.value [] account.emojis + ++ (case field.verified_at of + Just verified_at -> + [ span [ class "check-mark", title ("Verified at " ++ Common.formatDate verified_at) ] [ Common.icon "ok" ] ] + + Nothing -> + [ text "" ] + ) + ) + ) + account.fields + ) ] ] , counterLinks subView account diff --git a/src/View/AccountSelector.elm b/src/View/AccountSelector.elm index 84c6fb54..0b342089 100644 --- a/src/View/AccountSelector.elm +++ b/src/View/AccountSelector.elm @@ -34,7 +34,7 @@ accountIdentityView currentUser client = ( False, "" ) in li [ class <| "list-group-item account-selector-item " ++ entryClass ] - [ accountAvatar "" account + [ accountAvatar [ "" ] account , span [] [ strong [] (if account.display_name /= "" then diff --git a/src/View/Blocks.elm b/src/View/Blocks.elm index 4adc2ee7..b819f6d2 100644 --- a/src/View/Blocks.elm +++ b/src/View/Blocks.elm @@ -34,7 +34,7 @@ blockView currentUser account = in li [ class <| "list-group-item status " ++ entryClass ] [ div [ class "follow-entry" ] - [ Common.accountAvatarLink False account + [ Common.accountAvatarLink False Nothing account , div [ class "userinfo" ] [ strong [] [ a diff --git a/src/View/Common.elm b/src/View/Common.elm index 577e7c3b..be369288 100644 --- a/src/View/Common.elm +++ b/src/View/Common.elm @@ -6,6 +6,7 @@ module View.Common exposing , closeablePanelheading , confirmView , formatDate + , formatDateAndTime , icon , justifiedButtonGroup , loadMoreBtn @@ -23,9 +24,9 @@ import Types exposing (..) import View.Events exposing (..) -accountAvatar : String -> Account -> Html Msg -accountAvatar avatarClass account = - img [ class avatarClass, src account.avatar ] [] +accountAvatar : List String -> Account -> Html Msg +accountAvatar avatarClasses account = + img (src account.avatar :: (avatarClasses |> List.map (\avatarClass -> class avatarClass))) [] accountLink : Bool -> Account -> Html Msg @@ -45,8 +46,8 @@ accountLink external account = [ text <| "@" ++ account.acct ] -accountAvatarLink : Bool -> Account -> Html Msg -accountAvatarLink external account = +accountAvatarLink : Bool -> Maybe (List String) -> Account -> Html Msg +accountAvatarLink external cssClasses account = let accountHref = if external then @@ -55,19 +56,27 @@ accountAvatarLink external account = else href <| "#account/" ++ account.id - avatarClass = + externalClass = if external then "" else "avatar" + + avatarClasses = + case cssClasses of + Just classes -> + externalClass :: classes + + Nothing -> + [ externalClass ] in a [ href account.url , accountHref , title <| "@" ++ account.username ] - [ accountAvatar avatarClass account ] + [ accountAvatar avatarClasses account ] appLink : String -> Maybe Application -> Html Msg @@ -159,8 +168,8 @@ confirmView { message, onConfirm, onCancel } = ] -dateFormatter : Zone -> Posix -> String -dateFormatter = +dateAndTimeFormatter : Zone -> Posix -> String +dateAndTimeFormatter = DateFormat.format [ DateFormat.monthNameAbbreviated , DateFormat.text " " @@ -174,8 +183,26 @@ dateFormatter = ] +formatDateAndTime : String -> String +formatDateAndTime dateString = + Iso8601.toTime dateString + |> Result.withDefault (Time.millisToPosix 0) + |> dateAndTimeFormatter utc + + formatDate : String -> String formatDate dateString = + let + dateFormatter : Zone -> Posix -> String + dateFormatter = + DateFormat.format + [ DateFormat.monthNameAbbreviated + , DateFormat.text " " + , DateFormat.dayOfMonthSuffix + , DateFormat.text ", " + , DateFormat.yearNumber + ] + in Iso8601.toTime dateString |> Result.withDefault (Time.millisToPosix 0) |> dateFormatter utc diff --git a/src/View/Draft.elm b/src/View/Draft.elm index f4931841..d395a1c2 100644 --- a/src/View/Draft.elm +++ b/src/View/Draft.elm @@ -140,7 +140,7 @@ currentUserView currentUser = case currentUser of Just user -> div [ class "current-user" ] - [ Common.accountAvatarLink False user + [ Common.accountAvatarLink False Nothing user , div [ class "username" ] ((if user.display_name /= "" then getDisplayNameForAccount user diff --git a/src/View/Mutes.elm b/src/View/Mutes.elm index dc93473d..c84d73e7 100644 --- a/src/View/Mutes.elm +++ b/src/View/Mutes.elm @@ -34,7 +34,7 @@ muteView currentUser account = in li [ class <| "list-group-item status " ++ entryClass ] [ div [ class "follow-entry" ] - [ Common.accountAvatarLink False account + [ Common.accountAvatarLink False Nothing account , div [ class "userinfo" ] [ strong [] [ a diff --git a/src/View/Notification.elm b/src/View/Notification.elm index 372a309e..7a214f1d 100644 --- a/src/View/Notification.elm +++ b/src/View/Notification.elm @@ -87,7 +87,7 @@ notificationHeading accountsAndDate str iconType = in div [ class "status-info" ] [ div [ class "avatars" ] <| - List.map (Common.accountAvatarLink False) (List.map .account accountsAndDate) + List.map (Common.accountAvatarLink False Nothing) (List.map .account accountsAndDate) , p [ class "status-info-text" ] <| List.intersperse (text " ") [ Common.icon iconType @@ -120,7 +120,7 @@ notificationFollowView _ { accounts } = profileView : AccountNotificationDate -> Html Msg profileView { account, created_at } = div [ class "status follow-profile" ] - [ Common.accountAvatarLink False account + [ Common.accountAvatarLink False Nothing account , div [ class "username" ] [ Common.accountLink False account , span [ class "btn-sm follow-profile-date" ] diff --git a/src/View/Search.elm b/src/View/Search.elm index 2d384a67..f49543dc 100644 --- a/src/View/Search.elm +++ b/src/View/Search.elm @@ -15,7 +15,7 @@ accountListView accounts = let profileView account = li [ class "list-group-item status follow-profile" ] - [ Common.accountAvatarLink False account + [ Common.accountAvatarLink False Nothing account , div [ class "username" ] [ Common.accountLink False account ] , formatContent account.note [] |> div diff --git a/src/View/Status.elm b/src/View/Status.elm index f7200c62..f3b7b8f8 100644 --- a/src/View/Status.elm +++ b/src/View/Status.elm @@ -154,14 +154,14 @@ statusActionsView status currentUser showApp = [ class baseBtnClasses , href (Maybe.withDefault "#" sourceStatus.url) , target "_blank" - , title (sourceStatus.edited_at |> Maybe.map (\edited_at -> "Edited - " ++ Common.formatDate edited_at) |> Maybe.withDefault "") + , title (sourceStatus.edited_at |> Maybe.map (\edited_at -> "Edited - " ++ Common.formatDateAndTime edited_at) |> Maybe.withDefault "") ] - [ Common.icon "time", text <| Common.formatDate sourceStatus.created_at ] + [ Common.icon "time", text <| Common.formatDateAndTime sourceStatus.created_at ] , case sourceStatus.edited_at of Just edited_at -> em [ class baseBtnClasses - , title <| "Edited - " ++ Common.formatDate edited_at + , title <| "Edited - " ++ Common.formatDateAndTime edited_at ] [ text "Edited *" ] @@ -274,7 +274,7 @@ statusView context ({ account, reblog } as status) = Nothing -> div [ class "status" ] - [ Common.accountAvatarLink False account + [ Common.accountAvatarLink False Nothing account , div [ class "username" ] [ a accountLinkAttributes (getDisplayNameForAccount account