parent
d32f12bf20
commit
44f8caa1ea
@ -4,43 +4,6 @@ body {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
height: 8px
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #444;
|
||||
border: 0px none #ffffff;
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #777;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:active {
|
||||
background: #777;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
border: 0px none #ffffff;
|
||||
border-radius: 0;
|
||||
background: rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track:hover {
|
||||
background: #2a2e31;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track:active {
|
||||
background: #2a2e31;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-corner {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.timeline {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
@ -193,6 +156,10 @@ body {
|
||||
color: #d56344;
|
||||
}
|
||||
|
||||
.btn-delete .glyphicon {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
/* Attachments */
|
||||
|
||||
.attachments {
|
||||
@ -441,3 +408,42 @@ body {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* Scrollbars */
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
height: 8px
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: #444;
|
||||
border: 0px none #ffffff;
|
||||
border-radius: 50px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background: #777;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:active {
|
||||
background: #777;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
border: 0px none #ffffff;
|
||||
border-radius: 0;
|
||||
background: rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track:hover {
|
||||
background: #2a2e31;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track:active {
|
||||
background: #2a2e31;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-corner {
|
||||
background: transparent;
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ module Mastodon.ApiUrl
|
||||
, userAccount
|
||||
, account
|
||||
, accountTimeline
|
||||
, status
|
||||
, homeTimeline
|
||||
, publicTimeline
|
||||
, notifications
|
||||
@ -97,6 +98,11 @@ reblog server id =
|
||||
statuses server ++ "/" ++ (toString id) ++ "/reblog"
|
||||
|
||||
|
||||
status : Server -> Int -> String
|
||||
status server id =
|
||||
statuses server ++ "/" ++ (toString id)
|
||||
|
||||
|
||||
unreblog : Server -> Int -> String
|
||||
unreblog server id =
|
||||
statuses server ++ "/" ++ (toString id) ++ "/unreblog"
|
||||
|
@ -1,9 +1,11 @@
|
||||
module Mastodon.Helper
|
||||
exposing
|
||||
( extractReblog
|
||||
( accountMentioned
|
||||
, extractReblog
|
||||
, aggregateNotifications
|
||||
, addNotificationToAggregates
|
||||
, notificationToAggregate
|
||||
, sameAccount
|
||||
)
|
||||
|
||||
import List.Extra exposing (groupWhile, uniqueBy)
|
||||
@ -139,3 +141,13 @@ aggregateNotifications notifications =
|
||||
|> List.concat
|
||||
|> List.sortBy .created_at
|
||||
|> List.reverse
|
||||
|
||||
|
||||
accountMentioned : Mastodon.Model.Account -> Mastodon.Model.Mention -> Bool
|
||||
accountMentioned { acct, username } mention =
|
||||
acct == mention.acct && username == mention.username
|
||||
|
||||
|
||||
sameAccount : Mastodon.Model.Account -> Mastodon.Model.Account -> Bool
|
||||
sameAccount { acct, username } account =
|
||||
acct == account.acct && username == account.username
|
||||
|
@ -16,6 +16,7 @@ module Mastodon.Http
|
||||
, fetchGlobalTimeline
|
||||
, fetchUserTimeline
|
||||
, postStatus
|
||||
, deleteStatus
|
||||
, userAccount
|
||||
, send
|
||||
)
|
||||
@ -148,6 +149,13 @@ postStatus client statusRequestBody =
|
||||
|> HttpBuilder.withJsonBody (statusRequestBodyEncoder statusRequestBody)
|
||||
|
||||
|
||||
deleteStatus : Client -> Int -> Request Int
|
||||
deleteStatus client id =
|
||||
HttpBuilder.delete (ApiUrl.status client.server id)
|
||||
|> HttpBuilder.withExpect (Http.expectJson <| Decode.succeed id)
|
||||
|> HttpBuilder.withHeader "Authorization" ("Bearer " ++ client.token)
|
||||
|
||||
|
||||
context : Client -> Int -> Request Context
|
||||
context client id =
|
||||
HttpBuilder.get (ApiUrl.context client.server id)
|
||||
|
@ -52,6 +52,7 @@ type MastodonMsg
|
||||
| Notifications (Result Mastodon.Model.Error (List Mastodon.Model.Notification))
|
||||
| GlobalTimeline (Result Mastodon.Model.Error (List Mastodon.Model.Status))
|
||||
| Reblogged (Result Mastodon.Model.Error Mastodon.Model.Status)
|
||||
| StatusDeleted (Result Mastodon.Model.Error Int)
|
||||
| StatusPosted (Result Mastodon.Model.Error Mastodon.Model.Status)
|
||||
| Unreblogged (Result Mastodon.Model.Error Mastodon.Model.Status)
|
||||
| Account (Result Mastodon.Model.Error Mastodon.Model.Account)
|
||||
@ -70,6 +71,7 @@ type Msg
|
||||
| ClearOpenedAccount
|
||||
| CloseThread
|
||||
| DomResult (Result Dom.Error ())
|
||||
| DeleteStatus Int
|
||||
| DraftEvent DraftMsg
|
||||
| LoadAccount Int
|
||||
| MastodonEvent MastodonMsg
|
||||
@ -287,22 +289,18 @@ truncate entries =
|
||||
List.take maxBuffer entries
|
||||
|
||||
|
||||
accountMentioned : Mastodon.Model.Account -> Mastodon.Model.Mention -> Bool
|
||||
accountMentioned { acct, username } mention =
|
||||
acct == mention.acct && username == mention.username
|
||||
|
||||
|
||||
sameAccount : Mastodon.Model.Account -> Mastodon.Model.Account -> Bool
|
||||
sameAccount { acct, username } account =
|
||||
acct == account.acct && username == account.username
|
||||
|
||||
|
||||
postStatus : Mastodon.Model.Client -> Mastodon.Model.StatusRequestBody -> Cmd Msg
|
||||
postStatus client draft =
|
||||
Mastodon.Http.postStatus client draft
|
||||
|> Mastodon.Http.send (MastodonEvent << StatusPosted)
|
||||
|
||||
|
||||
deleteStatus : Mastodon.Model.Client -> Int -> Cmd Msg
|
||||
deleteStatus client id =
|
||||
Mastodon.Http.deleteStatus client id
|
||||
|> Mastodon.Http.send (MastodonEvent << StatusDeleted)
|
||||
|
||||
|
||||
errorText : Mastodon.Model.Error -> String
|
||||
errorText error =
|
||||
case error of
|
||||
@ -405,12 +403,12 @@ updateDraft draftMsg currentUser draft =
|
||||
let
|
||||
mentions =
|
||||
status.mentions
|
||||
|> List.filter (\m -> not (accountMentioned currentUser m))
|
||||
|> List.filter (\m -> not (Mastodon.Helper.accountMentioned currentUser m))
|
||||
|> List.map (\m -> "@" ++ m.acct)
|
||||
|> String.join " "
|
||||
|
||||
newStatus =
|
||||
if sameAccount status.account currentUser then
|
||||
if Mastodon.Helper.sameAccount status.account currentUser then
|
||||
mentions
|
||||
else
|
||||
"@" ++ status.account.acct ++ " " ++ mentions
|
||||
@ -544,6 +542,19 @@ processMastodonEvent msg model =
|
||||
StatusPosted _ ->
|
||||
{ model | draft = defaultDraft } ! []
|
||||
|
||||
StatusDeleted result ->
|
||||
case result of
|
||||
Ok id ->
|
||||
{ model
|
||||
| userTimeline = deleteStatusFromTimeline id model.userTimeline
|
||||
, localTimeline = deleteStatusFromTimeline id model.localTimeline
|
||||
, globalTimeline = deleteStatusFromTimeline id model.globalTimeline
|
||||
}
|
||||
! []
|
||||
|
||||
Err error ->
|
||||
{ model | errors = (errorText error) :: model.errors } ! []
|
||||
|
||||
Unreblogged result ->
|
||||
case result of
|
||||
Ok status ->
|
||||
@ -714,6 +725,14 @@ update msg model =
|
||||
CloseThread ->
|
||||
{ model | currentView = preferredTimeline model } ! []
|
||||
|
||||
DeleteStatus id ->
|
||||
case model.client of
|
||||
Just client ->
|
||||
model ! [ deleteStatus client id ]
|
||||
|
||||
Nothing ->
|
||||
model ! []
|
||||
|
||||
Reblog id ->
|
||||
-- Note: The case of reblogging is specific as it seems the server
|
||||
-- response takes a lot of time to be received by the client, so we
|
||||
|
122
src/View.elm
122
src/View.elm
@ -85,7 +85,12 @@ accountAvatarLink account =
|
||||
[ img [ class "avatar", src account.avatar ] [] ]
|
||||
|
||||
|
||||
attachmentPreview : String -> Maybe Bool -> List Mastodon.Model.Attachment -> Mastodon.Model.Attachment -> Html Msg
|
||||
attachmentPreview :
|
||||
String
|
||||
-> Maybe Bool
|
||||
-> List Mastodon.Model.Attachment
|
||||
-> Mastodon.Model.Attachment
|
||||
-> Html Msg
|
||||
attachmentPreview context sensitive attachments ({ url, preview_url } as attachment) =
|
||||
let
|
||||
nsfw =
|
||||
@ -202,13 +207,21 @@ statusView context ({ account, content, media_attachments, reblog, mentions } as
|
||||
]
|
||||
|
||||
|
||||
accountTimelineView : Mastodon.Model.Account -> List Mastodon.Model.Status -> String -> String -> Html Msg
|
||||
accountTimelineView :
|
||||
Mastodon.Model.Account
|
||||
-> List Mastodon.Model.Status
|
||||
-> String
|
||||
-> String
|
||||
-> Html Msg
|
||||
accountTimelineView account statuses label iconName =
|
||||
div [ class "col-md-3 column" ]
|
||||
[ div [ class "panel panel-default" ]
|
||||
[ closeablePanelheading iconName label ClearOpenedAccount
|
||||
, div [ class "timeline" ]
|
||||
[ div [ class "account-detail", style [ ( "background-image", "url('" ++ account.header ++ "')" ) ] ]
|
||||
[ div
|
||||
[ class "account-detail"
|
||||
, style [ ( "background-image", "url('" ++ account.header ++ "')" ) ]
|
||||
]
|
||||
[ div [ class "opacity-layer" ]
|
||||
[ img [ src account.avatar ] []
|
||||
, span [ class "account-display-name" ] [ text account.display_name ]
|
||||
@ -245,8 +258,8 @@ accountTimelineView account statuses label iconName =
|
||||
]
|
||||
|
||||
|
||||
statusActionsView : Mastodon.Model.Status -> Html Msg
|
||||
statusActionsView status =
|
||||
statusActionsView : Mastodon.Model.Status -> Mastodon.Model.Account -> Html Msg
|
||||
statusActionsView status currentUser =
|
||||
let
|
||||
targetStatus =
|
||||
Mastodon.Helper.extractReblog status
|
||||
@ -294,17 +307,23 @@ statusActionsView status =
|
||||
, onClickWithPreventAndStop favEvent
|
||||
]
|
||||
[ icon "star", text (toString status.favourites_count) ]
|
||||
, a
|
||||
[ class baseBtnClasses
|
||||
, href status.url
|
||||
, onClickWithPreventAndStop <| OpenThread status
|
||||
, if Mastodon.Helper.sameAccount status.account currentUser then
|
||||
a
|
||||
[ class <| baseBtnClasses ++ " btn-delete"
|
||||
, href ""
|
||||
, onClickWithPreventAndStop <| DeleteStatus status.id
|
||||
]
|
||||
[ icon "trash" ]
|
||||
else
|
||||
text ""
|
||||
, a
|
||||
[ class baseBtnClasses, href status.url, target "_blank" ]
|
||||
[ icon "time", formatDate ]
|
||||
]
|
||||
|
||||
|
||||
statusEntryView : String -> String -> Mastodon.Model.Status -> Html Msg
|
||||
statusEntryView context className status =
|
||||
statusEntryView : String -> String -> Mastodon.Model.Account -> Mastodon.Model.Status -> Html Msg
|
||||
statusEntryView context className currentUser status =
|
||||
let
|
||||
nsfwClass =
|
||||
case status.sensitive of
|
||||
@ -316,19 +335,25 @@ statusEntryView context className status =
|
||||
in
|
||||
li [ class <| "list-group-item " ++ className ++ " " ++ nsfwClass ]
|
||||
[ statusView context status
|
||||
, statusActionsView status
|
||||
, statusActionsView status currentUser
|
||||
]
|
||||
|
||||
|
||||
timelineView : String -> String -> String -> List Mastodon.Model.Status -> Html Msg
|
||||
timelineView label iconName context statuses =
|
||||
timelineView :
|
||||
String
|
||||
-> String
|
||||
-> String
|
||||
-> Mastodon.Model.Account
|
||||
-> List Mastodon.Model.Status
|
||||
-> Html Msg
|
||||
timelineView label iconName context currentUser statuses =
|
||||
div [ class "col-md-3 column" ]
|
||||
[ div [ class "panel panel-default" ]
|
||||
[ a
|
||||
[ href "", onClickWithPreventAndStop <| ScrollColumn context ]
|
||||
[ div [ class "panel-heading" ] [ icon iconName, text label ] ]
|
||||
, ul [ id context, class "list-group timeline" ] <|
|
||||
List.map (statusEntryView context "") statuses
|
||||
List.map (statusEntryView context "" currentUser) statuses
|
||||
]
|
||||
]
|
||||
|
||||
@ -346,8 +371,13 @@ notificationHeading accounts str iconType =
|
||||
]
|
||||
|
||||
|
||||
notificationStatusView : String -> Mastodon.Model.Status -> Mastodon.Model.NotificationAggregate -> Html Msg
|
||||
notificationStatusView context status { type_, accounts } =
|
||||
notificationStatusView :
|
||||
String
|
||||
-> Mastodon.Model.Account
|
||||
-> Mastodon.Model.Status
|
||||
-> Mastodon.Model.NotificationAggregate
|
||||
-> Html Msg
|
||||
notificationStatusView context currentUser status { type_, accounts } =
|
||||
div [ class <| "notification " ++ type_ ]
|
||||
[ case type_ of
|
||||
"reblog" ->
|
||||
@ -359,12 +389,12 @@ notificationStatusView context status { type_, accounts } =
|
||||
_ ->
|
||||
text ""
|
||||
, statusView context status
|
||||
, statusActionsView status
|
||||
, statusActionsView status currentUser
|
||||
]
|
||||
|
||||
|
||||
notificationFollowView : Mastodon.Model.NotificationAggregate -> Html Msg
|
||||
notificationFollowView { accounts } =
|
||||
notificationFollowView : Mastodon.Model.Account -> Mastodon.Model.NotificationAggregate -> Html Msg
|
||||
notificationFollowView currentUser { accounts } =
|
||||
let
|
||||
profileView account =
|
||||
div [ class "status follow-profile" ]
|
||||
@ -384,27 +414,30 @@ notificationFollowView { accounts } =
|
||||
]
|
||||
|
||||
|
||||
notificationEntryView : Mastodon.Model.NotificationAggregate -> Html Msg
|
||||
notificationEntryView notification =
|
||||
notificationEntryView :
|
||||
Mastodon.Model.Account
|
||||
-> Mastodon.Model.NotificationAggregate
|
||||
-> Html Msg
|
||||
notificationEntryView currentUser notification =
|
||||
li [ class "list-group-item" ]
|
||||
[ case notification.status of
|
||||
Just status ->
|
||||
notificationStatusView "notification" status notification
|
||||
notificationStatusView "notification" currentUser status notification
|
||||
|
||||
Nothing ->
|
||||
notificationFollowView notification
|
||||
notificationFollowView currentUser notification
|
||||
]
|
||||
|
||||
|
||||
notificationListView : List Mastodon.Model.NotificationAggregate -> Html Msg
|
||||
notificationListView notifications =
|
||||
notificationListView : Mastodon.Model.Account -> List Mastodon.Model.NotificationAggregate -> Html Msg
|
||||
notificationListView currentUser notifications =
|
||||
div [ class "col-md-3 column" ]
|
||||
[ div [ class "panel panel-default" ]
|
||||
[ a
|
||||
[ href "", onClickWithPreventAndStop <| ScrollColumn "notifications" ]
|
||||
[ div [ class "panel-heading" ] [ icon "bell", text "Notifications" ] ]
|
||||
, ul [ id "notifications", class "list-group timeline" ] <|
|
||||
List.map notificationEntryView notifications
|
||||
List.map (notificationEntryView currentUser) notifications
|
||||
]
|
||||
]
|
||||
|
||||
@ -561,8 +594,8 @@ draftView { draft, currentUser } =
|
||||
]
|
||||
|
||||
|
||||
threadView : Thread -> Html Msg
|
||||
threadView thread =
|
||||
threadView : Mastodon.Model.Account -> Thread -> Html Msg
|
||||
threadView currentUser thread =
|
||||
let
|
||||
statuses =
|
||||
List.concat
|
||||
@ -578,6 +611,7 @@ threadView thread =
|
||||
else
|
||||
""
|
||||
)
|
||||
currentUser
|
||||
status
|
||||
in
|
||||
div [ class "col-md-3 column" ]
|
||||
@ -614,23 +648,43 @@ sidebarView model =
|
||||
|
||||
homepageView : Model -> Html Msg
|
||||
homepageView model =
|
||||
case model.currentUser of
|
||||
Nothing ->
|
||||
text ""
|
||||
|
||||
Just currentUser ->
|
||||
div [ class "row" ]
|
||||
[ sidebarView model
|
||||
, timelineView "Home timeline" "home" "home" model.userTimeline
|
||||
, notificationListView model.notifications
|
||||
, timelineView
|
||||
"Home timeline"
|
||||
"home"
|
||||
"home"
|
||||
currentUser
|
||||
model.userTimeline
|
||||
, notificationListView currentUser model.notifications
|
||||
, case model.currentView of
|
||||
Model.LocalTimelineView ->
|
||||
timelineView "Local timeline" "th-large" "local" model.localTimeline
|
||||
timelineView
|
||||
"Local timeline"
|
||||
"th-large"
|
||||
"local"
|
||||
currentUser
|
||||
model.localTimeline
|
||||
|
||||
Model.GlobalTimelineView ->
|
||||
timelineView "Global timeline" "globe" "global" model.globalTimeline
|
||||
timelineView
|
||||
"Global timeline"
|
||||
"globe"
|
||||
"global"
|
||||
currentUser
|
||||
model.globalTimeline
|
||||
|
||||
Model.AccountView account ->
|
||||
-- Todo: Load the user timeline
|
||||
accountTimelineView account model.accountTimeline "Account" "user"
|
||||
|
||||
Model.ThreadView thread ->
|
||||
threadView thread
|
||||
threadView currentUser thread
|
||||
]
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user