Fix #97: Allow deleting a toot. (#102)

This commit is contained in:
Nicolas Perriault 2017-04-29 09:20:26 +02:00 committed by GitHub
parent d32f12bf20
commit 44f8caa1ea
6 changed files with 199 additions and 94 deletions

View File

@ -4,43 +4,6 @@ body {
overflow: hidden; 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 { .timeline {
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
@ -193,6 +156,10 @@ body {
color: #d56344; color: #d56344;
} }
.btn-delete .glyphicon {
font-size: 80%;
}
/* Attachments */ /* Attachments */
.attachments { .attachments {
@ -441,3 +408,42 @@ body {
width: 100%; 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;
}

View File

@ -6,6 +6,7 @@ module Mastodon.ApiUrl
, userAccount , userAccount
, account , account
, accountTimeline , accountTimeline
, status
, homeTimeline , homeTimeline
, publicTimeline , publicTimeline
, notifications , notifications
@ -97,6 +98,11 @@ reblog server id =
statuses server ++ "/" ++ (toString id) ++ "/reblog" statuses server ++ "/" ++ (toString id) ++ "/reblog"
status : Server -> Int -> String
status server id =
statuses server ++ "/" ++ (toString id)
unreblog : Server -> Int -> String unreblog : Server -> Int -> String
unreblog server id = unreblog server id =
statuses server ++ "/" ++ (toString id) ++ "/unreblog" statuses server ++ "/" ++ (toString id) ++ "/unreblog"

View File

@ -1,9 +1,11 @@
module Mastodon.Helper module Mastodon.Helper
exposing exposing
( extractReblog ( accountMentioned
, extractReblog
, aggregateNotifications , aggregateNotifications
, addNotificationToAggregates , addNotificationToAggregates
, notificationToAggregate , notificationToAggregate
, sameAccount
) )
import List.Extra exposing (groupWhile, uniqueBy) import List.Extra exposing (groupWhile, uniqueBy)
@ -139,3 +141,13 @@ aggregateNotifications notifications =
|> List.concat |> List.concat
|> List.sortBy .created_at |> List.sortBy .created_at
|> List.reverse |> 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

View File

@ -16,6 +16,7 @@ module Mastodon.Http
, fetchGlobalTimeline , fetchGlobalTimeline
, fetchUserTimeline , fetchUserTimeline
, postStatus , postStatus
, deleteStatus
, userAccount , userAccount
, send , send
) )
@ -148,6 +149,13 @@ postStatus client statusRequestBody =
|> HttpBuilder.withJsonBody (statusRequestBodyEncoder 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 -> Int -> Request Context
context client id = context client id =
HttpBuilder.get (ApiUrl.context client.server id) HttpBuilder.get (ApiUrl.context client.server id)

View File

@ -52,6 +52,7 @@ type MastodonMsg
| Notifications (Result Mastodon.Model.Error (List Mastodon.Model.Notification)) | Notifications (Result Mastodon.Model.Error (List Mastodon.Model.Notification))
| GlobalTimeline (Result Mastodon.Model.Error (List Mastodon.Model.Status)) | GlobalTimeline (Result Mastodon.Model.Error (List Mastodon.Model.Status))
| Reblogged (Result Mastodon.Model.Error 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) | StatusPosted (Result Mastodon.Model.Error Mastodon.Model.Status)
| Unreblogged (Result Mastodon.Model.Error Mastodon.Model.Status) | Unreblogged (Result Mastodon.Model.Error Mastodon.Model.Status)
| Account (Result Mastodon.Model.Error Mastodon.Model.Account) | Account (Result Mastodon.Model.Error Mastodon.Model.Account)
@ -70,6 +71,7 @@ type Msg
| ClearOpenedAccount | ClearOpenedAccount
| CloseThread | CloseThread
| DomResult (Result Dom.Error ()) | DomResult (Result Dom.Error ())
| DeleteStatus Int
| DraftEvent DraftMsg | DraftEvent DraftMsg
| LoadAccount Int | LoadAccount Int
| MastodonEvent MastodonMsg | MastodonEvent MastodonMsg
@ -287,22 +289,18 @@ truncate entries =
List.take maxBuffer 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 : Mastodon.Model.Client -> Mastodon.Model.StatusRequestBody -> Cmd Msg
postStatus client draft = postStatus client draft =
Mastodon.Http.postStatus client draft Mastodon.Http.postStatus client draft
|> Mastodon.Http.send (MastodonEvent << StatusPosted) |> 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 : Mastodon.Model.Error -> String
errorText error = errorText error =
case error of case error of
@ -405,12 +403,12 @@ updateDraft draftMsg currentUser draft =
let let
mentions = mentions =
status.mentions status.mentions
|> List.filter (\m -> not (accountMentioned currentUser m)) |> List.filter (\m -> not (Mastodon.Helper.accountMentioned currentUser m))
|> List.map (\m -> "@" ++ m.acct) |> List.map (\m -> "@" ++ m.acct)
|> String.join " " |> String.join " "
newStatus = newStatus =
if sameAccount status.account currentUser then if Mastodon.Helper.sameAccount status.account currentUser then
mentions mentions
else else
"@" ++ status.account.acct ++ " " ++ mentions "@" ++ status.account.acct ++ " " ++ mentions
@ -544,6 +542,19 @@ processMastodonEvent msg model =
StatusPosted _ -> StatusPosted _ ->
{ model | draft = defaultDraft } ! [] { 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 -> Unreblogged result ->
case result of case result of
Ok status -> Ok status ->
@ -714,6 +725,14 @@ update msg model =
CloseThread -> CloseThread ->
{ model | currentView = preferredTimeline model } ! [] { model | currentView = preferredTimeline model } ! []
DeleteStatus id ->
case model.client of
Just client ->
model ! [ deleteStatus client id ]
Nothing ->
model ! []
Reblog id -> Reblog id ->
-- Note: The case of reblogging is specific as it seems the server -- 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 -- response takes a lot of time to be received by the client, so we

View File

@ -85,7 +85,12 @@ accountAvatarLink account =
[ img [ class "avatar", src account.avatar ] [] ] [ 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) = attachmentPreview context sensitive attachments ({ url, preview_url } as attachment) =
let let
nsfw = 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 = accountTimelineView account statuses label iconName =
div [ class "col-md-3 column" ] div [ class "col-md-3 column" ]
[ div [ class "panel panel-default" ] [ div [ class "panel panel-default" ]
[ closeablePanelheading iconName label ClearOpenedAccount [ closeablePanelheading iconName label ClearOpenedAccount
, div [ class "timeline" ] , 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" ] [ div [ class "opacity-layer" ]
[ img [ src account.avatar ] [] [ img [ src account.avatar ] []
, span [ class "account-display-name" ] [ text account.display_name ] , 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 : Mastodon.Model.Status -> Mastodon.Model.Account -> Html Msg
statusActionsView status = statusActionsView status currentUser =
let let
targetStatus = targetStatus =
Mastodon.Helper.extractReblog status Mastodon.Helper.extractReblog status
@ -294,17 +307,23 @@ statusActionsView status =
, onClickWithPreventAndStop favEvent , onClickWithPreventAndStop favEvent
] ]
[ icon "star", text (toString status.favourites_count) ] [ icon "star", text (toString status.favourites_count) ]
, if Mastodon.Helper.sameAccount status.account currentUser then
a
[ class <| baseBtnClasses ++ " btn-delete"
, href ""
, onClickWithPreventAndStop <| DeleteStatus status.id
]
[ icon "trash" ]
else
text ""
, a , a
[ class baseBtnClasses [ class baseBtnClasses, href status.url, target "_blank" ]
, href status.url
, onClickWithPreventAndStop <| OpenThread status
]
[ icon "time", formatDate ] [ icon "time", formatDate ]
] ]
statusEntryView : String -> String -> Mastodon.Model.Status -> Html Msg statusEntryView : String -> String -> Mastodon.Model.Account -> Mastodon.Model.Status -> Html Msg
statusEntryView context className status = statusEntryView context className currentUser status =
let let
nsfwClass = nsfwClass =
case status.sensitive of case status.sensitive of
@ -316,19 +335,25 @@ statusEntryView context className status =
in in
li [ class <| "list-group-item " ++ className ++ " " ++ nsfwClass ] li [ class <| "list-group-item " ++ className ++ " " ++ nsfwClass ]
[ statusView context status [ statusView context status
, statusActionsView status , statusActionsView status currentUser
] ]
timelineView : String -> String -> String -> List Mastodon.Model.Status -> Html Msg timelineView :
timelineView label iconName context statuses = 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 "col-md-3 column" ]
[ div [ class "panel panel-default" ] [ div [ class "panel panel-default" ]
[ a [ a
[ href "", onClickWithPreventAndStop <| ScrollColumn context ] [ href "", onClickWithPreventAndStop <| ScrollColumn context ]
[ div [ class "panel-heading" ] [ icon iconName, text label ] ] [ div [ class "panel-heading" ] [ icon iconName, text label ] ]
, ul [ id context, class "list-group timeline" ] <| , 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 :
notificationStatusView context status { type_, accounts } = String
-> Mastodon.Model.Account
-> Mastodon.Model.Status
-> Mastodon.Model.NotificationAggregate
-> Html Msg
notificationStatusView context currentUser status { type_, accounts } =
div [ class <| "notification " ++ type_ ] div [ class <| "notification " ++ type_ ]
[ case type_ of [ case type_ of
"reblog" -> "reblog" ->
@ -359,12 +389,12 @@ notificationStatusView context status { type_, accounts } =
_ -> _ ->
text "" text ""
, statusView context status , statusView context status
, statusActionsView status , statusActionsView status currentUser
] ]
notificationFollowView : Mastodon.Model.NotificationAggregate -> Html Msg notificationFollowView : Mastodon.Model.Account -> Mastodon.Model.NotificationAggregate -> Html Msg
notificationFollowView { accounts } = notificationFollowView currentUser { accounts } =
let let
profileView account = profileView account =
div [ class "status follow-profile" ] div [ class "status follow-profile" ]
@ -384,27 +414,30 @@ notificationFollowView { accounts } =
] ]
notificationEntryView : Mastodon.Model.NotificationAggregate -> Html Msg notificationEntryView :
notificationEntryView notification = Mastodon.Model.Account
-> Mastodon.Model.NotificationAggregate
-> Html Msg
notificationEntryView currentUser notification =
li [ class "list-group-item" ] li [ class "list-group-item" ]
[ case notification.status of [ case notification.status of
Just status -> Just status ->
notificationStatusView "notification" status notification notificationStatusView "notification" currentUser status notification
Nothing -> Nothing ->
notificationFollowView notification notificationFollowView currentUser notification
] ]
notificationListView : List Mastodon.Model.NotificationAggregate -> Html Msg notificationListView : Mastodon.Model.Account -> List Mastodon.Model.NotificationAggregate -> Html Msg
notificationListView notifications = notificationListView currentUser notifications =
div [ class "col-md-3 column" ] div [ class "col-md-3 column" ]
[ div [ class "panel panel-default" ] [ div [ class "panel panel-default" ]
[ a [ a
[ href "", onClickWithPreventAndStop <| ScrollColumn "notifications" ] [ href "", onClickWithPreventAndStop <| ScrollColumn "notifications" ]
[ div [ class "panel-heading" ] [ icon "bell", text "Notifications" ] ] [ div [ class "panel-heading" ] [ icon "bell", text "Notifications" ] ]
, ul [ id "notifications", class "list-group timeline" ] <| , 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 : Mastodon.Model.Account -> Thread -> Html Msg
threadView thread = threadView currentUser thread =
let let
statuses = statuses =
List.concat List.concat
@ -578,6 +611,7 @@ threadView thread =
else else
"" ""
) )
currentUser
status status
in in
div [ class "col-md-3 column" ] div [ class "col-md-3 column" ]
@ -614,24 +648,44 @@ sidebarView model =
homepageView : Model -> Html Msg homepageView : Model -> Html Msg
homepageView model = homepageView model =
div [ class "row" ] case model.currentUser of
[ sidebarView model Nothing ->
, timelineView "Home timeline" "home" "home" model.userTimeline text ""
, notificationListView model.notifications
, case model.currentView of
Model.LocalTimelineView ->
timelineView "Local timeline" "th-large" "local" model.localTimeline
Model.GlobalTimelineView -> Just currentUser ->
timelineView "Global timeline" "globe" "global" model.globalTimeline div [ class "row" ]
[ sidebarView model
, timelineView
"Home timeline"
"home"
"home"
currentUser
model.userTimeline
, notificationListView currentUser model.notifications
, case model.currentView of
Model.LocalTimelineView ->
timelineView
"Local timeline"
"th-large"
"local"
currentUser
model.localTimeline
Model.AccountView account -> Model.GlobalTimelineView ->
-- Todo: Load the user timeline timelineView
accountTimelineView account model.accountTimeline "Account" "user" "Global timeline"
"globe"
"global"
currentUser
model.globalTimeline
Model.ThreadView thread -> Model.AccountView account ->
threadView thread -- Todo: Load the user timeline
] accountTimelineView account model.accountTimeline "Account" "user"
Model.ThreadView thread ->
threadView currentUser thread
]
authView : Model -> Html Msg authView : Model -> Html Msg