website/frontend/src/Pages/Debate/Arguments.elm
2025-01-03 18:29:59 -06:00

617 lines
18 KiB
Elm
Executable file

module Pages.Debate.Arguments exposing (Model, Msg, page)
import Browser.Events as Events
import Config.Data.Identity exposing (pageNames)
import Config.Data.ImageFolders as M
exposing
( ImageFolder(..)
, imagePathMaker
)
import Config.Helpers.Cards.Inner.StrengthBar exposing (barMaker)
import Config.Helpers.Cards.Inner.Text
exposing
( bodyFormat
, detailBodyLink
, detailBodyMaker
, detailFormat
, detailSpacing
, detailTitleMaker
, generalButton
, getHoverColours
)
import Config.Helpers.Cards.Inner.ToolTip exposing (tooltip)
import Config.Helpers.Cards.Outer.Helpers exposing (cardMaker)
import Config.Helpers.Cards.Outer.Types as C exposing (Cardable(..))
import Config.Helpers.Converters exposing (toTitleCase)
import Config.Helpers.Headers.Helpers exposing (headerMaker)
import Config.Helpers.Headers.Records exposing (argumentHeader)
import Config.Helpers.Headers.Types as R exposing (Headerable(..))
import Config.Helpers.Response
exposing
( pageList
, topLevelContainer
)
import Config.Helpers.Viewport exposing (resetViewport)
import Config.Pages.Debate.Arguments.List
exposing
( argumentList
)
import Config.Pages.Debate.Arguments.Types exposing (Argument)
import Config.Style.Colour.Helpers
exposing
( ThemeColor(..)
, getThemeColor
)
import Config.Style.Icons.Icons
exposing
( code
, copyLink
, thumbsDown
)
import Config.Style.Icons.Types as TySvg
exposing
( InnerPart
, OuterPart
)
import Config.Style.Images
exposing
( ElementSize(..)
, imageSquareMaker
)
import Config.Style.Transitions exposing (transitionStyleSlow)
import Effect exposing (Effect)
import Element as E
exposing
( Attribute
, Device
, DeviceClass(..)
, Element
, Orientation(..)
, alignLeft
, alignRight
, alignTop
, centerX
, centerY
, clip
, column
, el
, fill
, focused
, height
, htmlAttribute
, maximum
, none
, paddingEach
, paddingXY
, paragraph
, rgba
, row
, spacing
, text
, width
)
import Element.Background as B exposing (color)
import Element.Border as D
exposing
( color
, rounded
, shadow
)
import Element.Font as F
exposing
( bold
, center
, color
)
import Element.Input as Input
import Html.Attributes as H exposing (style)
import Layouts
import Page exposing (Page)
import Ports
import Process
import Route exposing (Route)
import Route.Path as Path
import Shared
import Svg.Attributes as SvgAttr
import Task
import View exposing (View)
page : Shared.Model -> Route () -> Page Model Msg
page shared route =
Page.new
{ init = init
, update = update
, subscriptions = subscriptions
, view = view shared
}
|> Page.withLayout toLayout
toLayout : Model -> Layouts.Layout Msg
toLayout model =
Layouts.Navbar {}
-- INIT
type alias Model =
{ hasBeenCopied : List Bool }
init : () -> ( Model, Effect Msg )
init () =
( { hasBeenCopied = List.repeat (List.length argumentList) False }
, Effect.batch
[ Effect.map
(\_ -> NoOp)
(Effect.sendCmd resetViewport)
, Effect.none
]
)
-- UPDATE
type Msg
= CopyText String Int
| ResetCopyState Int
| NoOp
update : Msg -> Model -> ( Model, Effect Msg )
update msg model =
let
urlLinkClickUpdate : Int -> Model
urlLinkClickUpdate index =
{ model
| hasBeenCopied =
List.take index model.hasBeenCopied
++ (case List.head (List.drop index model.hasBeenCopied) of
Just isCLicked ->
[ not isCLicked ]
Nothing ->
[]
-- shouldn't happen
)
++ List.drop (index + 1) model.hasBeenCopied
}
in
case msg of
CopyText text index ->
( urlLinkClickUpdate index
, Effect.batch
[ Effect.sendCmd (Ports.copyToClipboard text)
, Effect.sendCmd (Process.sleep 700 |> Task.perform (\_ -> ResetCopyState index))
]
)
ResetCopyState index ->
( urlLinkClickUpdate index
-- wrong lol
, Effect.none
)
NoOp ->
( model
, Effect.none
)
-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions model =
Sub.none
-- VIEW
view : Shared.Model -> Model -> View Msg
view shared model =
{ title = "debate (" ++ pageNames.pageArguments ++ ")"
, attributes = []
, element = debateContainer shared model
}
debateContainer : Shared.Model -> Model -> Element Msg
debateContainer shared model =
topLevelContainer (debateList shared model)
debateList : Shared.Model -> Model -> Element Msg
debateList shared model =
column
(case ( shared.device.class, shared.device.orientation ) of
_ ->
pageList shared.device
)
<|
List.concat
[ [ headerMaker (R.Arguments argumentHeader) ]
, List.map3
(\argument dummy index ->
cardMaker shared.device
(C.Argument argument)
(contentList shared model dummy index argument)
)
argumentList
model.hasBeenCopied
(List.range 0 (List.length argumentList))
]
contentList : Shared.Model -> Model -> Bool -> Int -> Argument -> List (Element Msg)
contentList shared model isLinkClicked index argument =
let
image : ElementSize -> Element msg
image size =
el
[ alignLeft
, alignTop
, paddingEach
{ top = 0
, right = 10
, bottom = 0
, left = 0
}
]
<|
imageSquareMaker shared.device (imagePathMaker M.Argument argument.argumentImage) True size
in
[ row
[ width fill
, paddingEach
{ top =
case ( shared.device.class, shared.device.orientation ) of
( Phone, Portrait ) ->
8
( Tablet, Portrait ) ->
8
_ ->
0
, right = 0
, bottom = 0
, left = 0
}
]
[ detailFormat column
[ detailFormat paragraph
[ case ( shared.device.class, shared.device.orientation ) of
( Phone, Portrait ) ->
none
( Tablet, Portrait ) ->
none
_ ->
image Medium
, el ([ height fill ] ++ bodyFormat TextLightGrey) <| text argument.propositionSummary
]
, detailFormat row
[ strengthMaker shared
, barMaker shared getConfidenceTooltip argument.argumentCertainty
]
]
]
, tableMaker shared.device argument
, formalizationMaker shared.device argument
, el [ alignRight ] <|
row [ width fill, spacing 20 ]
[ generalButton shared (Path.toString Path.Contact_Criticism) thumbsDown
, generalButton shared argument.proofLink code
, copyButton shared model isLinkClicked index argument
]
]
copyButton : Shared.Model -> Model -> Bool -> Int -> Argument -> Element Msg
copyButton shared model isLinkClicked index argument =
Input.button
[ focused
[ D.color (rgba 0 0 0 0)
, D.shadow
{ blur = 0
, color = rgba 0 0 0 0
, offset = ( 0, 0 )
, size = 0
}
]
]
{ onPress = Just (CopyText ("https://uprootnutrition.com" ++ Path.toString Path.Debate_Arguments ++ "#" ++ argument.argumentImage) index)
, label =
el
(if isLinkClicked then
[ transitionStyleSlow
, tooltip "Copied!" False
, case ( shared.device.class, shared.device.orientation ) of
( Phone, Portrait ) ->
B.color (getThemeColor BackgroundDarkGrey)
( Tablet, Portrait ) ->
B.color (getThemeColor BackgroundDarkGrey)
_ ->
B.color (getThemeColor BackgroundDeepDarkGrey)
, paddingXY 7 7
, D.rounded 10
, F.color (getThemeColor TextLightOrange)
]
else
[ transitionStyleSlow
, B.color (getThemeColor Transparent)
, paddingXY 7 7
, D.rounded 10
, F.color (getThemeColor TextLightOrange)
]
++ getHoverColours TextLightOrange
)
<|
copyLink
{ elementAttributes =
[]
, sharedModel = shared
, svgAttributes = [ SvgAttr.width "20" ]
}
}
strengthMaker : Shared.Model -> Element msg
strengthMaker shared =
el
(case ( shared.device.class, shared.device.orientation ) of
( Phone, Portrait ) ->
[]
( Tablet, Portrait ) ->
[]
_ ->
if not shared.isNavbarExpanded then
[ tooltip
"This represents my confidence that the argument is sound."
True
]
else
[]
)
<|
detailTitleMaker TextLightOrange "Confidence:"
getConfidenceTooltip : Int -> String
getConfidenceTooltip num =
case num of
0 ->
"Extremely low. Speculative reasoning."
1 ->
"Very low. Extremely weak reasoning."
2 ->
"Low. Weak reasoning."
3 ->
"Kinda low. Somewhat weak reasoning."
4 ->
"Below average. More weak than strong."
5 ->
"Moderate. OK reasoning."
6 ->
"Above average. More strong than weak."
7 ->
"Kinda high. Somewhat strong reasoning."
8 ->
"High. Robust reasoning."
9 ->
"Very high. Extremely robust reasoning."
10 ->
"Extremely high. Air tight reasoning."
_ ->
"Confidence level out of expected range."
tableMaker : Device -> Argument -> Element msg
tableMaker device argument =
let
cellPadding : Attribute msg
cellPadding =
paddingXY 10 5
in
column
[ centerX
, E.width fill
]
[ el
[ E.width fill
, htmlAttribute <| H.style "position" "relative"
]
<|
E.table
([ D.rounded 10
, D.width 2
, D.color (getThemeColor TextDarkGrey)
, clip
]
++ (case ( device.class, device.orientation ) of
( Phone, Portrait ) ->
[ B.color (getThemeColor BackgroundSpreadsheet) ]
( Tablet, Portrait ) ->
[ B.color (getThemeColor BackgroundSpreadsheet) ]
_ ->
[]
)
)
{ data = argument.definitionTable
, columns =
[ { header =
el
[ F.bold
, D.widthEach
{ bottom = 1
, top = 1
, left = 1
, right = 1
}
, D.color (getThemeColor TextDarkGrey)
, cellPadding
, E.width fill
]
<|
detailTitleMaker
TextLightOrange
"Definiendum"
, width = fill |> maximum 30
, view =
\definition ->
el
[ D.widthEach
{ bottom = 1
, top = 0
, left = 1
, right = 1
}
, D.color (getThemeColor TextDarkGrey)
, cellPadding
, E.height fill
]
<|
el
[ centerX
, centerY
]
<|
paragraph [] [ detailTitleMaker TextLightOrange definition.definiendum ]
}
, { header =
el
[ D.widthEach
{ bottom = 1
, top = 1
, left = 0
, right = 1
}
, D.color (getThemeColor TextDarkGrey)
, cellPadding
, E.width fill
]
<|
detailTitleMaker TextLightOrange "Definiens"
, width = fill
, view =
\definition ->
el
[ D.widthEach
{ bottom = 1
, top = 0
, left = 0
, right = 1
}
, D.color (getThemeColor TextDarkGrey)
, cellPadding
, E.height fill
]
<|
el [] <|
paragraph [] [ detailBodyMaker TextLightGrey (text definition.definiens) ]
}
]
}
]
formalizationMaker : Device -> Argument -> Element msg
formalizationMaker device argument =
column
[ centerX
, E.width fill
, spacing 10
]
(List.indexedMap
(\index argumentEntry ->
column
[ paddingXY
(case ( device.class, device.orientation ) of
( Phone, Portrait ) ->
0
( Tablet, Portrait ) ->
0
_ ->
40
)
3
]
(List.indexedMap
(\entryIndex entryWithNotation ->
column
[ centerX
, F.center
, detailSpacing
, E.width fill
]
[ paragraph
[ width fill ]
[ detailTitleMaker
TextLightOrange
(if entryIndex < List.length argumentEntry.premises then
"P" ++ String.fromInt (entryIndex + 1) ++ ") "
else
"C) "
)
, detailBodyMaker TextLightGrey
(text
(if entryIndex < List.length argumentEntry.premises then
entryWithNotation.premise
else
argumentEntry.conclusion
)
)
|> el
[]
]
, paragraph
[]
[ detailTitleMaker
TextLightOrange
(if entryIndex < List.length argumentEntry.premises then
"(" ++ entryWithNotation.notation ++ ")"
else
"(" ++ argumentEntry.conclusionNotation ++ ")"
)
]
]
)
(argumentEntry.premises ++ [ { premise = argumentEntry.conclusion, notation = argumentEntry.conclusionNotation } ])
)
)
argument.argumentFormalization
)