Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion elm-git.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"git-dependencies": {
"direct": {
"https://github.com/unisonweb/ui-core": "58d63fe0de338b9355a37262ccc8ed9fb486b806"
"https://github.com/unisonweb/ui-core": "e1cee63aac897c3c6c76ad5a2017f061eb73d978"
},
"indirect": {}
}
Expand Down
12 changes: 12 additions & 0 deletions src/UnisonShare/Api.elm
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,18 @@ projectBranchReleaseNotes projectRef branchRef =
}


projectBranchHistory : ProjectRef -> BranchRef -> Endpoint
projectBranchHistory projectRef branchRef =
let
( handle, slug ) =
ProjectRef.toApiStringParts projectRef
in
GET
{ path = [ "users", handle, "projects", slug, "history", BranchRef.toApiUrlString branchRef ]
, queryParams = []
}



-- PROJECT ROLE ASSIGNMENTS (COLLABORATORS)

Expand Down
5 changes: 5 additions & 0 deletions src/UnisonShare/Link.elm
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,11 @@ projectOverview projectRef_ =
toClick (Route.projectOverview projectRef_)


projectHistory : ProjectRef -> Maybe BranchRef -> PageCursorParam -> Click msg
projectHistory projectRef_ branchRef_ cursor =
toClick (Route.projectHistory projectRef_ branchRef_ cursor)


projectBranches : ProjectRef -> PageCursorParam -> Click msg
projectBranches projectRef_ cursor =
toClick (Route.projectBranches projectRef_ cursor)
Expand Down
351 changes: 351 additions & 0 deletions src/UnisonShare/Page/ProjectHistoryPage.elm
Original file line number Diff line number Diff line change
@@ -0,0 +1,351 @@
module UnisonShare.Page.ProjectHistoryPage exposing (..)

import Code.BranchRef exposing (BranchRef)
import Code.FullyQualifiedName as FQN
import Code.Hash as Hash
import Html exposing (Html, div, h3, label, p, span, text)
import Html.Attributes exposing (class)
import Lib.Util as Util
import RemoteData exposing (RemoteData(..), WebData)
import String.Extra exposing (pluralize)
import UI
import UI.AnchoredOverlay as AnchoredOverlay
import UI.Card as Card
import UI.DateTime as DateTime
import UI.Icon as Icon exposing (Icon)
import UI.PageContent as PageContent exposing (PageContent)
import UI.PageLayout as PageLayout exposing (PageLayout)
import UI.PageTitle as PageTitle
import UI.Placeholder as Placeholder
import UI.ProfileSnippet as ProfileSnippet
import UnisonShare.AppContext exposing (AppContext)
import UnisonShare.Link as Link
import UnisonShare.Page.ErrorPage as ErrorPage
import UnisonShare.PageFooter as PageFooter
import UnisonShare.Paginated as Paginated exposing (Paginated(..))
import UnisonShare.Project as Project exposing (ProjectDetails)
import UnisonShare.Project.BranchHistory as BranchHistory exposing (HistoryEntry)
import UnisonShare.Project.ProjectRef exposing (ProjectRef)
import UnisonShare.Route as Route
import UnisonShare.SwitchBranch as SwitchBranch



-- MODEL


type alias PaginatedBranchHistory =
Paginated.Paginated HistoryEntry


type alias Model =
{ history : WebData PaginatedBranchHistory
, switchBranch : SwitchBranch.Model
}


preInit : Model
preInit =
{ history = NotAsked
, switchBranch = SwitchBranch.init
}


init : AppContext -> ProjectDetails -> Maybe BranchRef -> Paginated.PageCursorParam -> ( Model, Cmd Msg )
init appContext project branchRef cursor =
let
branchRef_ =
Maybe.withDefault (Project.defaultBrowsingBranch project) branchRef
in
( { history = Loading
, switchBranch = SwitchBranch.init
}
, fetchProjectBranchHistory appContext project.ref branchRef_ cursor
)



-- UPDATE


type Msg
= FetchProjectBranchHistoryFinished BranchRef (WebData PaginatedBranchHistory)
| SwitchBranchMsg SwitchBranch.Msg


update : AppContext -> ProjectDetails -> Maybe BranchRef -> Msg -> Model -> ( Model, Cmd Msg )
update appContext project _ msg model =
case msg of
FetchProjectBranchHistoryFinished _ history ->
( { model | history = history }, Cmd.none )

SwitchBranchMsg sbMsg ->
let
( switchBranch, switchBranchCmd, out ) =
SwitchBranch.update appContext project.ref sbMsg model.switchBranch

navCmd =
case out of
SwitchBranch.SwitchToBranchRequest branchRef ->
Route.navigate
appContext.navKey
(Route.projectHistory
project.ref
(Just branchRef)
Paginated.NoPageCursor
)

_ ->
Cmd.none
in
( { model | switchBranch = switchBranch }
, Cmd.batch
[ Cmd.map SwitchBranchMsg switchBranchCmd
, navCmd
]
)



-- EFFECTS


fetchProjectBranchHistory : AppContext -> ProjectRef -> BranchRef -> Paginated.PageCursorParam -> Cmd Msg
fetchProjectBranchHistory _ _ branchRef _ =
let
{-
params =
{ limit = 64
, cursor = cursor
}
-}
mkPaginated prev next items =
Paginated.Paginated { prev = prev, next = next, items = items }

mock =
RemoteData.Success
(mkPaginated
Nothing
(Just (Paginated.PageCursor "asdf-1234"))
[ BranchHistory.Comment
{ createdAt = DateTime.unsafeFromISO8601 "2025-11-03T13:35:33-05:00"
, afterCausalHash = Hash.unsafeFromString "commenthash1"
, author = BranchHistory.UnverifiedUser { name = "Simon" }
, subject = Just "Just fixed a bunch of stuff"
, body = "And it was really cool"
}
, BranchHistory.Changeset
{ hash = Hash.unsafeFromString "namespacehash3"
, updates =
[ { hash = Hash.unsafeFromString "definitionhash1"
, fqn = FQN.fromString "List.map"
}
]
, removes = []
, aliases =
[ { hash = Hash.unsafeFromString "definitionhash1"
, fqn = FQN.fromString "List.map"
}
]
, renames = []
}
, BranchHistory.Comment
{ createdAt = DateTime.unsafeFromISO8601 "2025-11-03T13:35:33-05:00"
, afterCausalHash = Hash.unsafeFromString "commenthash1"
, author = BranchHistory.UnverifiedUser { name = "Simon" }
, subject = Just "Just fixed a bunch of stuff"
, body = "And it was really cool"
}
]
)
in
Util.delayMsg 500 (FetchProjectBranchHistoryFinished branchRef mock)



{-
ShareApi.projectBranchHistory projectRef branchRef
|> HttpApi.toRequest
BranchHistory.decode
(RemoteData.fromResult >> FetchProjectBranchHistoryFinished branchRef)
|> HttpApi.perform appContext.api
-}
-- VIEW


viewLoadingPage : PageLayout msg
viewLoadingPage =
let
shape length =
Placeholder.text
|> Placeholder.withLength length
|> Placeholder.subdued
|> Placeholder.tiny
|> Placeholder.view

content =
PageContent.oneColumn
[ Card.card
[ shape Placeholder.Large
, shape Placeholder.Small
, shape Placeholder.Medium
]
|> Card.asContained
|> Card.view
]
|> PageContent.withPageTitle (PageTitle.title "History")
in
PageLayout.centeredNarrowLayout content PageFooter.pageFooter
|> PageLayout.withSubduedBackground


viewHistoryEntry_ : String -> Icon msg -> { leftTitle : Html msg, rightTitle : Html msg } -> List (Html msg) -> Html msg
viewHistoryEntry_ className icon { leftTitle, rightTitle } cardContent =
div [ class ("history-entry " ++ className) ]
[ div [ class "history-entry_gutter" ]
[ div [ class "icon-wrapper" ] [ Icon.view icon ]
]
, div [ class "history-entry_title-and-details" ]
[ div [ class "history-entry_title" ]
[ leftTitle
, rightTitle
]
, Card.card cardContent
|> Card.withClassName "project-history_entry"
|> Card.asContained
|> Card.view
]
]


viewHistoryEntry : AppContext -> HistoryEntry -> Html msg
viewHistoryEntry appContext entry =
case entry of
BranchHistory.Comment comment ->
let
createdAt =
DateTime.view
(DateTime.DistanceFrom appContext.now)
appContext.timeZone
comment.createdAt

author =
case comment.author of
BranchHistory.VerifiedShareUser user ->
ProfileSnippet.view (ProfileSnippet.profileSnippet user)

BranchHistory.UnverifiedUser { name } ->
span [ class "unverified-user" ] [ text name ]
in
viewHistoryEntry_
"history-entry_comment"
Icon.tag
{ leftTitle = author, rightTitle = createdAt }
[ comment.subject
|> Maybe.map (\s -> h3 [] [ text s ])
|> Maybe.withDefault UI.nothing
, p [] [ text comment.body ]
]

BranchHistory.Changeset changeset ->
let
numChanges =
(changeset.updates ++ changeset.removes ++ changeset.aliases ++ changeset.renames)
|> List.length
|> pluralize "change" "changes"

viewChanges : String -> Icon msg -> String -> List BranchHistory.Change -> Html msg
viewChanges className icon title changes =
if List.isEmpty changes then
UI.nothing

else
div [ class className ]
[ div [ class "history-entry_changeset_changes_title" ]
[ Icon.view icon, label [] [ text title ] ]
, div [ class "history-entry_changeset_changes_list" ]
(List.map (.fqn >> FQN.view) changes)
]

viewCardContent : BranchHistory.ChangesetDetails -> Html msg
viewCardContent { updates, removes, aliases, renames } =
div [ class "history-entry_changeset_changes" ]
[ viewChanges "updates" Icon.largePlus "Adds/Updates" updates
, viewChanges "removes" Icon.trash "Removes" removes
, viewChanges "aliases" Icon.tags "Aliases" aliases
, viewChanges "renames" Icon.tag "Renames" renames
]
in
viewHistoryEntry_
"history-entry_changeset"
Icon.historyNode
{ leftTitle = Hash.view changeset.hash, rightTitle = text numChanges }
[ viewCardContent changeset ]


viewPaginationControls : ProjectRef -> Maybe BranchRef -> { a | prev : Maybe Paginated.PageCursor, next : Maybe Paginated.PageCursor } -> Html msg
viewPaginationControls projectRef branchRef cursors =
let
toLink cursor =
Link.projectHistory projectRef branchRef cursor
in
Paginated.view toLink cursors


viewPageContent : AppContext -> ProjectDetails -> Maybe BranchRef -> SwitchBranch.Model -> PaginatedBranchHistory -> PageContent Msg
viewPageContent appContext project branchRef switchBranch (Paginated history) =
let
branchRef_ =
Maybe.withDefault (Project.defaultBrowsingBranch project) branchRef

entries =
if List.isEmpty history.items then
[ div [] [ text "No entries" ] ]

else
List.map (viewHistoryEntry appContext) history.items
in
PageContent.oneColumn
[ div [ class "history-entries" ] entries
, viewPaginationControls project.ref branchRef history
]
|> PageContent.withPageTitle
(PageTitle.title "History"
|> PageTitle.withRightSide
[ SwitchBranch.toAnchoredOverlay project.ref
branchRef_
False
switchBranch
|> AnchoredOverlay.map SwitchBranchMsg
|> AnchoredOverlay.view
]
)


view :
AppContext
-> ProjectDetails
-> Maybe BranchRef
-> Paginated.PageCursorParam
-> Model
-> ( PageLayout Msg, Maybe (Html Msg) )
view appContext project branchRef _ model =
case model.history of
NotAsked ->
( viewLoadingPage, Nothing )

Loading ->
( viewLoadingPage, Nothing )

Success history ->
( PageLayout.centeredNarrowLayout
(viewPageContent appContext project branchRef model.switchBranch history)
PageFooter.pageFooter
|> PageLayout.withSubduedBackground
, Nothing
)

Failure e ->
( ErrorPage.view appContext.session e "history" "project-history-page"
, Nothing
)
Loading
Loading