summaryrefslogtreecommitdiff
path: root/elm3
diff options
context:
space:
mode:
Diffstat (limited to 'elm3')
-rw-r--r--elm3/CharEdit/General.elm260
-rw-r--r--elm3/CharEdit/Main.elm130
-rw-r--r--elm3/CharEdit/New.elm19
-rw-r--r--elm3/CharEdit/Traits.elm81
-rw-r--r--elm3/CharEdit/VN.elm186
-rw-r--r--elm3/DocEdit.elm107
-rw-r--r--elm3/Lib/Api.elm110
-rw-r--r--elm3/Lib/Autocomplete.elm292
-rw-r--r--elm3/Lib/Editsum.elm59
-rw-r--r--elm3/Lib/Ffi.elm29
-rw-r--r--elm3/Lib/Html.elm182
-rw-r--r--elm3/Lib/RDate.elm84
-rw-r--r--elm3/Lib/Util.elm76
-rw-r--r--elm3/Lightbox.elm178
-rw-r--r--elm3/ProdEdit/General.elm78
-rw-r--r--elm3/ProdEdit/Main.elm158
-rw-r--r--elm3/ProdEdit/Names.elm81
-rw-r--r--elm3/ProdEdit/New.elm12
-rw-r--r--elm3/ProdEdit/Relations.elm79
-rw-r--r--elm3/RelEdit/General.elm272
-rw-r--r--elm3/RelEdit/Main.elm137
-rw-r--r--elm3/RelEdit/New.elm19
-rw-r--r--elm3/RelEdit/Producers.elm92
-rw-r--r--elm3/RelEdit/Vn.elm77
-rw-r--r--elm3/StaffEdit/Main.elm220
-rw-r--r--elm3/StaffEdit/New.elm12
-rw-r--r--elm3/UVNList/Options.elm37
-rw-r--r--elm3/UVNList/Status.elm77
-rw-r--r--elm3/UVNList/Vote.elm103
-rw-r--r--elm3/User/Login.elm86
-rw-r--r--elm3/User/PassReset.elm90
-rw-r--r--elm3/User/PassSet.elm94
-rw-r--r--elm3/User/Register.elm108
-rw-r--r--elm3/User/Settings.elm206
-rw-r--r--elm3/VNEdit/General.elm155
-rw-r--r--elm3/VNEdit/Main.elm199
-rw-r--r--elm3/VNEdit/New.elm12
-rw-r--r--elm3/VNEdit/Relations.elm89
-rw-r--r--elm3/VNEdit/Screenshots.elm182
-rw-r--r--elm3/VNEdit/Seiyuu.elm104
-rw-r--r--elm3/VNEdit/Staff.elm95
-rw-r--r--elm3/VNEdit/Titles.elm103
-rw-r--r--elm3/elm.json30
43 files changed, 0 insertions, 4800 deletions
diff --git a/elm3/CharEdit/General.elm b/elm3/CharEdit/General.elm
deleted file mode 100644
index ee0dbd77..00000000
--- a/elm3/CharEdit/General.elm
+++ /dev/null
@@ -1,260 +0,0 @@
-module CharEdit.General exposing (..)
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Html.Events exposing (..)
-import File exposing (File)
-import Lib.Html exposing (..)
-import Lib.Autocomplete as A
-import Lib.Util exposing (..)
-import Lib.Gen as Gen
-import Lib.Api as Api
-
-
-type alias Model =
- { alias : String
- , aliasDuplicates : Bool
- , bDay : Int
- , bMonth : Int
- , bloodt : String
- , desc : String
- , gender : String
- , height : Int
- , image : Int
- , imgState : Api.State
- , name : String
- , original : String
- , sBust : Int
- , sHip : Int
- , sWaist : Int
- , weight : Maybe Int
- , mainIs : Bool
- , mainInstance : Bool
- , mainId : Int
- , mainSpoil : Int
- , mainName : String
- , mainSearch : A.Model Gen.ApiCharResult
- }
-
-
-init : Gen.CharEdit -> Model
-init d =
- { alias = d.alias
- , aliasDuplicates = False
- , bDay = d.b_day
- , bMonth = d.b_month
- , bloodt = d.bloodt
- , desc = d.desc
- , gender = d.gender
- , height = d.height
- , image = d.image
- , imgState = Api.Normal
- , name = d.name
- , original = d.original
- , sBust = d.s_bust
- , sHip = d.s_hip
- , sWaist = d.s_waist
- , weight = d.weight
- , mainIs = d.main_is
- , mainInstance = isJust d.main
- , mainId = Maybe.withDefault 0 d.main
- , mainSpoil = d.main_spoil
- , mainName = d.main_name
- , mainSearch = A.init
- }
-
-
-new : Model
-new =
- { alias = ""
- , aliasDuplicates = False
- , bDay = 0
- , bMonth = 0
- , bloodt = "unknown"
- , desc = ""
- , gender = "unknown"
- , height = 0
- , image = 0
- , imgState = Api.Normal
- , name = ""
- , original = ""
- , sBust = 0
- , sHip = 0
- , sWaist = 0
- , weight = Nothing
- , mainIs = False
- , mainInstance = False
- , mainId = 0
- , mainSpoil = 0
- , mainName = ""
- , mainSearch = A.init
- }
-
-
-searchConfig : A.Config Msg Gen.ApiCharResult
-searchConfig = { wrap = MainSearch, id = "add-main", source = A.charSource }
-
-
-type Msg
- = Name String
- | Original String
- | Alias String
- | Desc String
- | Image String
- | Gender String
- | Bloodt String
- | BMonth String
- | BDay String
- | SBust String
- | SWaist String
- | SHip String
- | Height String
- | Weight String
- | MainInstance Bool
- | MainSpoil String
- | MainSearch (A.Msg Gen.ApiCharResult)
- | ImgUpload (List File)
- | ImgDone Api.Response
-
-
-update : Msg -> Model -> (Model, Cmd Msg)
-update msg model =
- case msg of
- Name s -> ({ model | name = s }, Cmd.none)
- Original s -> ({ model | original = s }, Cmd.none)
- Alias s -> ({ model | alias = s, aliasDuplicates = hasDuplicates (model.name :: model.original :: splitLn s) }, Cmd.none)
- Desc s -> ({ model | desc = s }, Cmd.none)
- Image s -> ({ model | image = if s == "" then 0 else Maybe.withDefault model.image (String.toInt s) }, Cmd.none)
- Gender s -> ({ model | gender = s }, Cmd.none)
- Bloodt s -> ({ model | bloodt = s }, Cmd.none)
- BMonth s -> ({ model | bMonth = if s == "" then 0 else Maybe.withDefault model.bMonth (String.toInt s) }, Cmd.none)
- BDay s -> ({ model | bDay = if s == "" then 0 else Maybe.withDefault model.bDay (String.toInt s) }, Cmd.none)
- SBust s -> ({ model | sBust = if s == "" then 0 else Maybe.withDefault model.sBust (String.toInt s) }, Cmd.none)
- SWaist s -> ({ model | sWaist = if s == "" then 0 else Maybe.withDefault model.sWaist (String.toInt s) }, Cmd.none)
- SHip s -> ({ model | sHip = if s == "" then 0 else Maybe.withDefault model.sHip (String.toInt s) }, Cmd.none)
- Height s -> ({ model | height = if s == "" then 0 else Maybe.withDefault model.height (String.toInt s) }, Cmd.none)
- Weight s -> ({ model | weight = String.toInt s }, Cmd.none)
- MainInstance b->({ model | mainInstance = b }, Cmd.none)
- MainSpoil s -> ({ model | mainSpoil = Maybe.withDefault model.mainSpoil (String.toInt s) }, Cmd.none)
- MainSearch m ->
- let (nm, c, res) = A.update searchConfig m model.mainSearch
- in case res of
- Nothing -> ({ model | mainSearch = nm }, c)
- Just r ->
- -- If the selected char has a main, automatically select that as our main
- let chr = Maybe.withDefault {id = r.id, name = r.name, original = r.original } r.main
- in ({ model | mainId = chr.id, mainName = chr.name, mainSearch = A.clear nm }, c)
-
- ImgUpload [i] -> ({ model | imgState = Api.Loading }, Api.postImage Api.Ch i ImgDone)
- ImgUpload _ -> (model, Cmd.none)
-
- ImgDone (Gen.Image id _ _) -> ({ model | image = id, imgState = Api.Normal }, Cmd.none)
- ImgDone r -> ({ model | image = 0, imgState = Api.Error r }, Cmd.none)
-
-
-zeroEmpty : Int -> String
-zeroEmpty i = if i == 0 then "" else String.fromInt i
-
-
-view : Model -> Html Msg
-view model = card "general" "General info" []
-
- [ cardRow "Name" Nothing <| formGroups
- [ [ label [for "name"] [text "Name (romaji)"]
- , inputText "name" model.name Name [required True, maxlength 200]
- ]
- , [ label [for "original"] [text "Original"]
- , inputText "original" model.original Original [maxlength 200]
- , div [class "form-group__help"] [text "The character's name in the language of the visual novel, leave blank if it already is in the Latin alphabet."]
- ]
- , [ inputTextArea "aliases" model.alias Alias
- [ rows 4, maxlength 500
- , classList [("is-invalid", model.aliasDuplicates)]
- ]
- , if model.aliasDuplicates
- then div [class "invalid-feedback"]
- [ text "There are duplicate aliases." ]
- else text ""
- , div [class "form-group__help"] [ text "(Un)official aliases, separated by a newline." ]
- ]
- ]
-
- , cardRow "Description" (Just "English please!") <| formGroup
- [ inputTextArea "desc" model.desc Desc [rows 8] ]
-
- , cardRow "Image" Nothing
- [ div [class "row"]
- [ div [class "col-md col-md--1"]
- [ div [style "max-width" "200px", style "margin-bottom" "8px"]
- [ dbImg "ch" (if model.imgState == Api.Loading then -1 else model.image) [] Nothing ]
- ]
- , div [class "col-md col-md--2"] <| formGroups
- [ [ label [for "img"] [ text "Upload new image" ]
- , input [type_ "file", class "text", name "img", id "img", Api.onFileChange ImgUpload, disabled (model.imgState == Api.Loading) ] []
- , case model.imgState of
- Api.Error r -> div [class "invalid-feedback"] [text <| Api.showResponse r]
- _ -> text ""
- , div [class "form-group__help"]
- [ text "Image must be in JPEG or PNG format and at most 1MiB. Images larger than 256x300 will be resized automatically. Image must be safe for work!" ]
- ]
- , [ label [for "img_id"] [ text "Image ID" ]
- , inputText "img_id" (String.fromInt model.image) Image [pattern "^[0-9]+$", disabled (model.imgState == Api.Loading)]
- , div [class "form-group__help"]
- [ text "Use a character image that is already on the server. Set to '0' to remove the current image." ]
- ]
- ]
- ]
- ]
-
- , cardRow "Meta" Nothing <| formGroups
- [ [ label [for "sex"] [text "Sex"]
- , inputSelect [id "sex", onInput Gender] model.gender Gen.genders
- ]
- , [ label [for "bloodt"] [text "Blood type"]
- , inputSelect [id "bloodt", onInput Bloodt] model.bloodt Gen.bloodTypes
- ]
- -- TODO: Enforce that both or neither are set
- , [ label [for "b_month"] [text "Birthday"]
- , inputSelect [id "b_month", onInput BMonth, class "form-control--inline"] (String.fromInt model.bMonth)
- <| ("0", "--month--") :: List.map (\i -> (String.fromInt i, String.fromInt i)) (List.range 1 12)
- , inputSelect [id "b_day", onInput BDay, class "form-control--inline"] (String.fromInt model.bDay)
- <| ("0", "--day--" ) :: List.map (\i -> (String.fromInt i, String.fromInt i)) (List.range 1 31)
- ]
- -- XXX: This looks messy
- , [ label [] [ text "Measurements" ]
- , p []
- [ text "Bust (cm): ", inputText "s_bust" (zeroEmpty model.sBust ) SBust [class "form-control--inline", style "width" "4em", pattern "^[0-9]{0,5}$"]
- , text " Waist (cm): ", inputText "s_waist" (zeroEmpty model.sWaist) SWaist [class "form-control--inline", style "width" "4em", pattern "^[0-9]{0,5}$"]
- , text " Hip (cm): ", inputText "s_hip" (zeroEmpty model.sHip ) SHip [class "form-control--inline", style "width" "4em", pattern "^[0-9]{0,5}$"]
- ]
- , p []
- [ text "Height (cm): ", inputText "height" (zeroEmpty model.height) Height [class "form-control--inline", style "width" "5em", pattern "^[0-9]{0,5}$"]
- , text " Weight (kg): ",inputText "weight" (Maybe.withDefault "" <| Maybe.map String.fromInt model.weight) Weight [class "form-control--inline", style "width" "5em", pattern "^[0-9]{0,5}$"]
- ]
- ]
- ]
-
- , cardRow "Instance" Nothing <|
- if model.mainIs
- then formGroup [ div [class "form-group__help"]
- [ text "This character is already referenced as \"main\" from another character entry."
- , text " If you want link this entry to another character, please edit that other character instead."
- ]
- ]
- else formGroups <|
- [ label [class "checkbox"] [ inputCheck "" model.mainInstance MainInstance, text " This character is an instance of another character" ] ]
- :: if not model.mainInstance then [] else
- [ [ if model.mainId == 0
- then div [] [ text "No character selected." ]
- else div []
- [ text "Main character: "
- , span [class "muted"] [ text <| "c" ++ String.fromInt model.mainId ++ ":" ]
- , a [href <| "/c" ++ String.fromInt model.mainId, target "_blank"] [ text model.mainName ]
- ]
- ]
- , if model.mainId == 0 then [] else
- [ inputSelect [id "mainspoil", onInput MainSpoil, class "form-control--inline"] (String.fromInt model.mainSpoil) spoilLevels ]
- , A.view searchConfig model.mainSearch [placeholder "Character name...", style "max-width" "400px"]
- ]
-
- ]
diff --git a/elm3/CharEdit/Main.elm b/elm3/CharEdit/Main.elm
deleted file mode 100644
index dbb88788..00000000
--- a/elm3/CharEdit/Main.elm
+++ /dev/null
@@ -1,130 +0,0 @@
-module CharEdit.Main exposing (Model,Msg,main,new,update,view)
-
-import Html exposing (..)
-import Html.Lazy exposing (..)
-import Browser
-import Browser.Navigation exposing (load)
-import Lib.Html exposing (..)
-import Lib.Gen as Gen
-import Lib.Api as Api
-import Lib.Editsum as Editsum
-import CharEdit.General as General
-import CharEdit.Traits as Traits
-import CharEdit.VN as VN
-
-
-main : Program Gen.CharEdit Model Msg
-main = Browser.element
- { init = \e -> (init e, Cmd.none)
- , view = view
- , update = update
- , subscriptions = always Sub.none
- }
-
-
-type alias Model =
- { state : Api.State
- , editsum : Editsum.Model
- , general : General.Model
- , traits : Traits.Model
- , vn : VN.Model
- , id : Maybe Int
- }
-
-
-init : Gen.CharEdit -> Model
-init d =
- { state = Api.Normal
- , editsum = { authmod = d.authmod, editsum = d.editsum, locked = d.locked, hidden = d.hidden }
- , general = General.init d
- , traits = Traits.init d.traits
- , vn = VN.init d.vns d.vnrels
- , id = d.id
- }
-
-
-new : List Gen.CharEditVns -> List Gen.CharEditVnrels -> Model
-new vns vnrels =
- { state = Api.Normal
- , editsum = Editsum.new
- , general = General.new
- , traits = Traits.init []
- , vn = VN.init vns vnrels
- , id = Nothing
- }
-
-
-encode : Model -> Gen.CharEditSend
-encode model =
- { editsum = model.editsum.editsum
- , hidden = model.editsum.hidden
- , locked = model.editsum.locked
- , alias = model.general.alias
- , b_day = model.general.bDay
- , b_month = model.general.bMonth
- , bloodt = model.general.bloodt
- , desc = model.general.desc
- , gender = model.general.gender
- , height = model.general.height
- , image = model.general.image
- , name = model.general.name
- , original = model.general.original
- , s_bust = model.general.sBust
- , s_hip = model.general.sHip
- , s_waist = model.general.sWaist
- , weight = model.general.weight
- , main = if not model.general.mainInstance || model.general.mainId == 0 then Nothing else Just model.general.mainId
- , main_spoil = model.general.mainSpoil
- , traits = List.map (\e -> { tid = e.tid, spoil = e.spoil }) model.traits.traits
- , vns = VN.encode model.vn
- }
-
-
-type Msg
- = Editsum Editsum.Msg
- | General General.Msg
- | Traits Traits.Msg
- | VN VN.Msg
- | Submit
- | Submitted Api.Response
-
-
-update : Msg -> Model -> (Model, Cmd Msg)
-update msg model =
- case msg of
- Editsum m -> ({ model | editsum = Editsum.update m model.editsum }, Cmd.none)
- General m -> let (nm, c) = General.update m model.general in ({ model | general = nm }, Cmd.map General c)
- Traits m -> let (nm, c) = Traits.update m model.traits in ({ model | traits = nm }, Cmd.map Traits c)
- VN m -> let (nm, c) = VN.update m model.vn in ({ model | vn = nm }, Cmd.map VN c)
-
- Submit ->
- let
- path =
- case model.id of
- Just id -> "/c" ++ String.fromInt id ++ "/edit"
- Nothing -> "/c/add"
- body = Gen.chareditSendEncode (encode model)
- in ({ model | state = Api.Loading }, Api.post path body Submitted)
-
- Submitted (Gen.Changed id rev) -> (model, load <| "/c" ++ String.fromInt id ++ "." ++ String.fromInt rev)
- Submitted r -> ({ model | state = Api.Error r }, Cmd.none)
-
-
-isValid : Model -> Bool
-isValid model = not
- ( model.general.aliasDuplicates
- || (model.general.mainInstance && model.general.mainId == 0)
- || model.traits.duplicates
- || model.vn.duplicates
- )
-
-
-view : Model -> Html Msg
-view model =
- form_ Submit (model.state == Api.Loading)
- [ Html.map General <| lazy General.view model.general
- , Html.map Traits <| lazy Traits.view model.traits
- , Html.map VN <| lazy VN.view model.vn
- , Html.map Editsum <| lazy Editsum.view model.editsum
- , submitButton "Submit" model.state (isValid model) False
- ]
diff --git a/elm3/CharEdit/New.elm b/elm3/CharEdit/New.elm
deleted file mode 100644
index 1a43341a..00000000
--- a/elm3/CharEdit/New.elm
+++ /dev/null
@@ -1,19 +0,0 @@
-module CharEdit.New exposing (main)
-
-import Browser
-import Lib.Gen exposing (CharEditVnrels, CharEditVns)
-import CharEdit.Main as Main
-
-
-type alias Flags =
- { vnrels : List CharEditVnrels
- , vns : List CharEditVns
- }
-
-main : Program Flags Main.Model Main.Msg
-main = Browser.element
- { init = \f -> (Main.new f.vns f.vnrels, Cmd.none)
- , view = Main.view
- , update = Main.update
- , subscriptions = always Sub.none
- }
diff --git a/elm3/CharEdit/Traits.elm b/elm3/CharEdit/Traits.elm
deleted file mode 100644
index 60399452..00000000
--- a/elm3/CharEdit/Traits.elm
+++ /dev/null
@@ -1,81 +0,0 @@
-module CharEdit.Traits exposing (Model, Msg, init, update, view)
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Html.Events exposing (..)
-import Lib.Html exposing (..)
-import Lib.Autocomplete as A
-import Lib.Gen as Gen
-import Lib.Util exposing (..)
-
-
-type alias Model =
- { traits : List Gen.CharEditTraits
- , search : A.Model Gen.ApiTraitResult
- , duplicates : Bool
- }
-
-
-init : List Gen.CharEditTraits -> Model
-init l =
- { traits = l
- , search = A.init
- , duplicates = False
- }
-
-
-type Msg
- = Del Int
- | SetSpoil Int String
- | Search (A.Msg Gen.ApiTraitResult)
-
-
-searchConfig : A.Config Msg Gen.ApiTraitResult
-searchConfig = { wrap = Search, id = "add-trait", source = A.traitSource }
-
-
-validate : Model -> Model
-validate model = { model | duplicates = hasDuplicates <| List.map .tid model.traits }
-
-
-update : Msg -> Model -> (Model, Cmd Msg)
-update msg model =
- case msg of
- Del i -> (validate { model | traits = delidx i model.traits }, Cmd.none)
- SetSpoil i s -> (validate { model | traits = modidx i (\e -> { e | spoil = Maybe.withDefault e.spoil (String.toInt s) }) model.traits }
- , Cmd.none )
-
- Search m ->
- let (nm, c, res) = A.update searchConfig m model.search
- in case res of
- Nothing -> ({ model | search = nm }, c)
- Just r ->
- let nrow = { tid = r.id, name = r.name, group = Maybe.withDefault "" r.group, spoil = 0 }
- in (validate { model | search = A.clear nm, traits = model.traits ++ [nrow] }, c)
-
-
-
-view : Model -> Html Msg
-view model =
- let
- entry n e = editListRow ""
- [ editListField 2 "col-form-label single-line"
- [ span [ class "muted" ] [ text <| e.group ++ " / " ]
- , a [href <| "/i" ++ String.fromInt e.tid, title e.name, target "_blank" ] [ text e.name ] ]
- , editListField 1 ""
- [ inputSelect [onInput (SetSpoil n)] (String.fromInt e.spoil) spoilLevels ]
- , editListField 0 "" [ removeButton (Del n) ]
- ]
-
- in card "traits" "Traits" []
- <| editList (List.indexedMap entry model.traits)
- ++ formGroups (
- (if model.duplicates
- then [ [ div [ class "invalid-feedback" ]
- [ text "There are duplicate traits." ] ] ]
- else []
- ) ++
- [ label [for "add-trait"] [text "Add trait"]
- :: A.view searchConfig model.search [placeholder "Trait", style "max-width" "400px"]
- ]
- )
diff --git a/elm3/CharEdit/VN.elm b/elm3/CharEdit/VN.elm
deleted file mode 100644
index 57a50c88..00000000
--- a/elm3/CharEdit/VN.elm
+++ /dev/null
@@ -1,186 +0,0 @@
-module CharEdit.VN exposing (Model, Msg, init, encode, update, view)
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Html.Events exposing (..)
-import Json.Encode as JE
-import Dict exposing (Dict)
-import Lib.Html exposing (..)
-import Lib.Autocomplete as A
-import Lib.Gen as Gen
-import Lib.Util exposing (..)
-import Lib.Api as Api
-
-
-type alias VNRel =
- { id : Int
- , title : String
- , role : String
- , spoil : Int
- , relsel : Bool
- , rel : Dict Int { role : String, spoil : Int }
- }
-
-type alias Model =
- { vn : List VNRel
- , releases : Dict Int (List Gen.CharEditVnrelsReleases) -- Mapping from VN id -> list of releases
- , search : A.Model Gen.ApiVNResult
- , duplicates : Bool
- }
-
-
-init : List Gen.CharEditVns -> List Gen.CharEditVnrels -> Model
-init vns rels =
- -- Turn the array from the server into a more usable data structure. This assumes that the array is ordered by VN id.
- let
- merge o n = case n.rid of
- Nothing -> { o | role = n.role, spoil = n.spoil }
- Just i -> { o | relsel = True, rel = Dict.insert i { role = n.role, spoil = n.spoil } o.rel }
-
- new n = case n.rid of
- Nothing -> { id = n.vid, title = n.title, relsel = False, role = n.role, spoil = n.spoil, rel = Dict.empty }
- Just i -> { id = n.vid, title = n.title, relsel = True, role = "", spoil = 0, rel = Dict.fromList [(i, { role = n.role, spoil = n.spoil })] }
-
- step n l =
- case l of
- [] -> [ new n ]
- i::xs ->
- if i.id == n.vid
- then merge i n :: xs
- else new n :: l
- in
- { vn = List.foldr step [] vns
- , releases = Dict.fromList <| List.map (\n -> (n.id, n.releases)) rels
- , search = A.init
- , duplicates = False
- }
-
-
--- XXX: The model and the UI allow an invalid state: VN is present, but
--- role="". This isn't too obvious to trigger, I hope, so in this case we'll
--- just be lazy and not send the VN to the server.
-encode : Model -> List Gen.CharEditSendVns
-encode model =
- let
- vn e =
- (if e.role == "" then [] else [{ vid = e.id, rid = Nothing, role = e.role, spoil = e.spoil }])
- ++
- (if e.relsel then Dict.foldl (\id r l -> { vid = e.id, rid = Just id, role = r.role, spoil = r.spoil } :: l) [] e.rel else [])
- in List.concat <| List.map vn model.vn
-
-
-
-type Msg
- = Del Int
- | SetSel Int Bool
- | SetRole Int String
- | SetSpoil Int String
- | SetRRole Int Int String
- | SetRSpoil Int Int String
- | Search (A.Msg Gen.ApiVNResult)
- | ReleaseInfo Int Api.Response
-
-
-searchConfig : A.Config Msg Gen.ApiVNResult
-searchConfig = { wrap = Search, id = "add-vn", source = A.vnSource }
-
-
-validate : Model -> Model
-validate model = { model | duplicates = hasDuplicates <| List.map .id model.vn }
-
-
-update : Msg -> Model -> (Model, Cmd Msg)
-update msg model =
- let
- rrole s o_ = if s == "" then Nothing
- else Just <| case o_ of
- Nothing -> { role = s, spoil = 0 }
- Just o -> { o | role = s }
- rspoil s = Maybe.map (\o -> { o | spoil = Maybe.withDefault o.spoil (String.toInt s) })
- in case msg of
- Del i -> (validate { model | vn = delidx i model.vn }, Cmd.none)
- SetSel i b -> ({ model | vn = modidx i (\e -> { e | relsel = b }) model.vn }, Cmd.none)
- SetRole i s -> ({ model | vn = modidx i (\e -> { e | role = s }) model.vn }, Cmd.none)
- SetSpoil i s -> ({ model | vn = modidx i (\e -> { e | spoil = Maybe.withDefault e.spoil (String.toInt s) }) model.vn }, Cmd.none)
- SetRRole i id s -> ({ model | vn = modidx i (\e -> { e | rel = Dict.update id (rrole s) e.rel }) model.vn }, Cmd.none)
- SetRSpoil i id s -> ({ model | vn = modidx i (\e -> { e | rel = Dict.update id (rspoil s) e.rel }) model.vn }, Cmd.none)
-
- Search m ->
- let (nm, c, res) = A.update searchConfig m model.search
- in case res of
- Nothing -> ({ model | search = nm }, c)
- Just r ->
- let
- nrow = { id = r.id, title = r.title, relsel = False, role = "primary", spoil = 0, rel = Dict.empty }
- nc = case Dict.get r.id model.releases of
- Nothing -> Api.post "/js/release.json" (JE.object [("vid", JE.int r.id)]) (ReleaseInfo r.id)
- Just _ -> Cmd.none
- in (validate { model | search = A.clear nm, vn = model.vn ++ [nrow] }, Cmd.batch [c, nc])
-
- ReleaseInfo vid (Gen.ReleaseResult r) -> ({ model | releases = Dict.insert vid r model.releases}, Cmd.none)
- ReleaseInfo _ _ -> (model, Cmd.none)
-
-
-
-view : Model -> Html Msg
-view model =
- let
- vn n e = editList <|
- editListRow ""
- [ editListField 3 "col-form-label single-line"
- [ span [ class "muted" ] [ text <| "v" ++ String.fromInt e.id ++ ":" ]
- , a [ href <| "/v" ++ String.fromInt e.id, target "_blank" ] [ text e.title ]
- ]
- , editListField 0 "" [ removeButton (Del n) ]
- ]
- :: case Dict.get e.id model.releases of
- Nothing -> [ div [ class "spinner spinner--md" ] [] ]
- Just l -> default n e :: if e.relsel then List.map (rel n e.rel) l else []
-
- default n e =
- editListRow ""
- [ editListField 2 "ml-3"
- [ label [class "checkbox"] [ inputCheck "" e.relsel (SetSel n), text " Per release" ] ]
- , editListField 1 "col-form-label single-line text-right" [ text <| if e.relsel then "Default:" else "All releases:" ]
- , editListField 1 ""
- [ inputSelect [onInput (SetRole n)] e.role <|
- (if e.relsel then [("", "Not involved")] else [])
- ++ Gen.charRoles
- ]
- , editListField 1 ""
- [ if e.role == ""
- then text ""
- else inputSelect [onInput (SetSpoil n)] (String.fromInt e.spoil) spoilLevels
- ]
- ]
-
- rel n rels e =
- let
- sel = Maybe.withDefault { role = "", spoil = 0 } <| Dict.get e.id rels
- in editListRow ""
- [ editListField 3 "col-form-label single-line ml-3" <|
- span [ class "muted" ] [ text <| "r" ++ String.fromInt e.id ++ ": " ]
- :: List.map iconLanguage e.lang
- ++
- [ a [href <| "/r" ++ String.fromInt e.id, title e.title, target "_blank" ] [ text e.title ] ]
- , editListField 1 ""
- [ inputSelect [onInput (SetRRole n e.id)] sel.role (("", "-default-") :: Gen.charRoles) ]
- , editListField 1 ""
- [ if sel.role == ""
- then text ""
- else inputSelect [onInput (SetRSpoil n e.id)] (String.fromInt sel.spoil) spoilLevels
- ]
- ]
-
- in card "vns" "Visual Novels" [] <|
- List.concat (List.indexedMap vn model.vn)
- ++ formGroups (
- (if model.duplicates
- then [ [ div [ class "invalid-feedback" ]
- [ text "There are duplicate visual novels." ] ] ]
- else []
- ) ++
- [ label [for "add-vn"] [text "Add visual novel"]
- :: A.view searchConfig model.search [placeholder "VIsual novel", style "max-width" "400px"]
- ]
- )
diff --git a/elm3/DocEdit.elm b/elm3/DocEdit.elm
deleted file mode 100644
index 4461c522..00000000
--- a/elm3/DocEdit.elm
+++ /dev/null
@@ -1,107 +0,0 @@
-module DocEdit exposing (main)
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Html.Events exposing (..)
-import Browser
-import Browser.Navigation exposing (load)
-import Json.Encode as JE
-import Lib.Html exposing (..)
-import Lib.Gen as Gen
-import Lib.Api as Api
-import Lib.Ffi as Ffi
-import Lib.Editsum as Editsum
-
-
-main : Program Gen.DocEdit Model Msg
-main = Browser.element
- { init = \e -> (init e, Cmd.none)
- , view = view
- , update = update
- , subscriptions = always Sub.none
- }
-
-
-type alias Model =
- { state : Api.State
- , editsum : Editsum.Model
- , title : String
- , content : String
- , id : Int
- , preview : String
- }
-
-
-init : Gen.DocEdit -> Model
-init d =
- { state = Api.Normal
- , editsum = { authmod = True, editsum = d.editsum, locked = d.locked, hidden = d.hidden }
- , title = d.title
- , content = d.content
- , id = d.id
- , preview = ""
- }
-
-
-encode : Model -> Gen.DocEditSend
-encode model =
- { editsum = model.editsum.editsum
- , hidden = model.editsum.hidden
- , locked = model.editsum.locked
- , title = model.title
- , content = model.content
- }
-
-
-type Msg
- = Editsum Editsum.Msg
- | Submit
- | Submitted Api.Response
- | Title String
- | Content String
- | Preview
- | HandlePreview Api.Response
-
-
-update : Msg -> Model -> (Model, Cmd Msg)
-update msg model =
- case msg of
- Editsum e -> ({ model | editsum = Editsum.update e model.editsum }, Cmd.none)
- Title s -> ({ model | title = s }, Cmd.none)
- Content s -> ({ model | content = s }, Cmd.none)
-
- Submit ->
- let
- path = "/d" ++ String.fromInt model.id ++ "/edit"
- body = Gen.doceditSendEncode (encode model)
- in ({ model | state = Api.Loading }, Api.post path body Submitted)
-
- Submitted (Gen.Changed id rev) -> (model, load <| "/d" ++ String.fromInt id ++ "." ++ String.fromInt rev)
- Submitted r -> ({ model | state = Api.Error r }, Cmd.none)
-
- Preview ->
- ( { model | state = Api.Loading, preview = "" }
- , Api.post "/js/markdown.json" (JE.object [("content", JE.string model.content)]) HandlePreview
- )
-
- HandlePreview (Gen.Content s) -> ({ model | state = Api.Normal, preview = s }, Cmd.none)
- HandlePreview r -> ({ model | state = Api.Error r }, Cmd.none)
-
-
-view : Model -> Html Msg
-view model =
- form_ Submit (model.state == Api.Loading)
- [ card "general" "General" [] <| formGroups
- [ [ label [ for "title" ] [ text "Title" ]
- , inputText "title" model.title Title [required True, maxlength 200]
- ]
- , [ label [ for "content" ] [ text "Content" ]
- , inputTextArea "content" model.content Content [rows 100, required True]
- ]
- , [ button [ type_ "button", class "btn", onClick Preview ] [ text "Preview" ]
- , div [ class "doc", Ffi.innerHtml model.preview ] []
- ]
- ]
- , Html.map Editsum (Editsum.view model.editsum)
- , submitButton "Submit" model.state True False
- ]
diff --git a/elm3/Lib/Api.elm b/elm3/Lib/Api.elm
deleted file mode 100644
index c1e0ddcb..00000000
--- a/elm3/Lib/Api.elm
+++ /dev/null
@@ -1,110 +0,0 @@
-module Lib.Api exposing (..)
-
-import Json.Encode as JE
-import Json.Decode as JD
-import File exposing (File)
-import Http
-import Html exposing (Attribute)
-import Html.Events exposing (on)
-
-import Lib.Gen exposing (ApiResponse(..), decodeApiResponse)
-
-
--- Handy state enum for forms
-type State
- = Normal
- | Loading
- | Error Response
-
-
-
-type alias Response = ApiResponse
-
-
--- User-friendly error message if the response isn't what the code expected.
--- (Technically a good chunk of this function could also be automatically
--- generated by ElmGen.pm, but that wouldn't really have all that much value).
-showResponse : Response -> String
-showResponse res =
- let unexp = "Unexpected response, please report a bug."
- in case res of
- HTTPError (Http.Timeout) -> "Network timeout, please try again later."
- HTTPError (Http.NetworkError) -> "Network error, please try again later."
- HTTPError (Http.BadStatus r) -> "Server error " ++ String.fromInt r ++ ", please try again later, or report an issue if this persists."
- HTTPError (Http.BadBody r) -> "Invalid response from the server, please report a bug (debug info: " ++ r ++")."
- HTTPError (Http.BadUrl _) -> unexp
- Success -> unexp
- CSRF -> "Invalid CSRF token, please refresh the page and try again."
- Throttled -> "Action throttled."
- Invalid _ -> "Invalid form data, please report a bug." -- This error is already logged server-side, no debug info necessary
- Unauth -> "You do not have the permission to perform this action."
- BadEmail -> "Unknown email address."
- BadLogin -> "Invalid username or password."
- BadPass -> "Your chosen password is in a database of leaked passwords, please choose another one."
- Bot -> "Invalid answer to the anti-bot question."
- Taken -> "Username already taken, please choose a different name."
- DoubleEmail -> "Email address already used for another account."
- DoubleIP -> "You can only register one account from the same IP within 24 hours."
- Unchanged -> "No changes"
- Changed _ _ -> unexp
- VNResult _ -> unexp
- StaffResult _ -> unexp
- ProducerResult _ -> unexp
- CharResult _ -> unexp
- TraitResult _ -> unexp
- ReleaseResult _ -> unexp
- ImgFormat -> "Unrecognized image format, please upload a JPG or PNG file."
- Image _ _ _ -> unexp
- Content _ -> unexp
-
-
-expectResponse : (Response -> msg) -> Http.Expect msg
-expectResponse msg =
- let
- res r = msg <| case r of
- Err e -> HTTPError e
- Ok v -> v
- in Http.expectJson res decodeApiResponse
-
-
--- Send a POST request with a JSON body to the VNDB API and get a Response back.
-post : String -> JE.Value -> (Response -> msg) -> Cmd msg
-post url body msg =
- Http.post
- { url = url
- , body = Http.jsonBody body
- , expect = expectResponse msg
- }
-
-
-
--- Simple image upload API
-
-type ImageType
- = Cv
- | Sf
- | Ch
-
-
-onFileChange : (List File -> m) -> Attribute m
-onFileChange msg = on "change" <| JD.map msg <| JD.at ["target","files"] <| JD.list File.decoder
-
-
--- Upload an image to /js/imageupload.json
-postImage : ImageType -> File -> (Response -> msg) -> Cmd msg
-postImage ty file msg =
- let
- tys = case ty of
- Cv -> "cv"
- Sf -> "sf"
- Ch -> "ch"
-
- body = Http.multipartBody
- [ Http.stringPart "type" tys
- , Http.filePart "img" file
- ]
- in Http.post
- { url = "/js/imageupload.json"
- , body = body
- , expect = expectResponse msg
- }
diff --git a/elm3/Lib/Autocomplete.elm b/elm3/Lib/Autocomplete.elm
deleted file mode 100644
index facbb8d2..00000000
--- a/elm3/Lib/Autocomplete.elm
+++ /dev/null
@@ -1,292 +0,0 @@
-module Lib.Autocomplete exposing
- ( Config
- , SourceConfig
- , Model
- , Msg
- , staffSource
- , vnSource
- , producerSource
- , charSource
- , traitSource
- , init
- , clear
- , update
- , view
- )
-
-import Html exposing (..)
-import Html.Events exposing (..)
-import Html.Attributes exposing (..)
-import Html.Keyed as Keyed
-import Json.Encode as JE
-import Json.Decode as JD
-import Task
-import Process
-import Browser.Dom as Dom
-import Lib.Html exposing (..)
-import Lib.Api as Api
-import Lib.Gen as Gen
-
-
-type alias Config m a =
- -- How to wrap a Msg from this model into a Msg of the using model
- { wrap : Msg a -> m
- -- A unique 'id' of the input box (necessary for the blur/focus events)
- , id : String
- -- The source defines where to get autocomplete results from and how to display them
- , source : SourceConfig m a
- }
-
-
-type alias SourceConfig m a =
- -- API path to query for completion results.
- -- (The API must accept POST requests with {"search":".."} as body)
- { path : String
- -- How to decode results from the API
- , decode : Api.Response -> Maybe (List a)
- -- How to display the decoded results
- , view : a -> List (Html m)
- -- Unique ID of an item (must not be an empty string).
- -- This is used to remember selection across data refreshes and to optimize
- -- HTML generation.
- , key : a -> String
- }
-
-
-
-staffSource : SourceConfig m Gen.ApiStaffResult
-staffSource =
- { path = "/js/staff.json"
- , decode = \x -> case x of
- Gen.StaffResult e -> Just e
- _ -> Nothing
- , view = (\i -> [ div [ class "row row-compact" ]
- [ div [ class "col single-line muted" ] [ text <| "s" ++ String.fromInt i.id ]
- , div [ class "col col--2 single-line semi-bold" ] [ text i.name ]
- , div [ class "col col--2 single-line" ] [ text i.original ]
- ] ] )
- , key = .aid >> String.fromInt
- }
-
-
-vnSource : SourceConfig m Gen.ApiVNResult
-vnSource =
- { path = "/js/vn.json"
- , decode = \x -> case x of
- Gen.VNResult e -> Just e
- _ -> Nothing
- , view = (\i -> [ div [ class "row row-compact" ]
- [ div [ class "col single-line muted" ] [ text <| "v" ++ String.fromInt i.id ]
- , div [ class "col col--4 single-line semi-bold" ] [ text i.title ]
- ] ] )
- , key = .id >> String.fromInt
- }
-
-
-producerSource : SourceConfig m Gen.ApiProducerResult
-producerSource =
- { path = "/js/producer.json"
- , decode = \x -> case x of
- Gen.ProducerResult e -> Just e
- _ -> Nothing
- , view = (\i -> [ div [ class "row row-compact" ]
- [ div [ class "col single-line muted" ] [ text <| "p" ++ String.fromInt i.id ]
- , div [ class "col col--4 single-line semi-bold" ] [ text i.name ]
- ] ] )
- , key = .id >> String.fromInt
- }
-
-
-charSource : SourceConfig m Gen.ApiCharResult
-charSource =
- { path = "/js/char.json"
- , decode = \x -> case x of
- Gen.CharResult e -> Just e
- _ -> Nothing
- , view = (\i -> [ div [ class "row row-compact" ]
- [ div [ class "col single-line muted" ] [ text <| "c" ++ String.fromInt i.id ]
- , div [ class "col col--2 single-line semi-bold" ] [ text i.name ]
- , div [ class "col col--2 single-line" ] [ text i.original ]
- ] ] )
- , key = .id >> String.fromInt
- }
-
-
-traitSource : SourceConfig m Gen.ApiTraitResult
-traitSource =
- { path = "/js/trait.json"
- , decode = \x -> case x of
- Gen.TraitResult e -> Just e
- _ -> Nothing
- , view = (\i -> [ div [ class "row row-compact" ]
- [ div [ class "col single-line muted" ] [ text <| "i" ++ String.fromInt i.id ]
- , div [ class "col col--4 single-line" ]
- [ span [ class "muted" ] [ text <| (Maybe.withDefault "" i.group) ++ " / " ]
- , span [ class "semi-bold" ] [ text i.name ]
- ]
- ] ] )
- , key = .id >> String.fromInt
- }
-
-
-
-type alias Model a =
- { position : Maybe Dom.Element
- , value : String
- , results : List a
- , sel : String
- , loading : Bool
- , wait : Int
- }
-
-
-init : Model a
-init =
- { position = Nothing
- , value = ""
- , results = []
- , sel = ""
- , loading = False
- , wait = 0
- }
-
-
-clear : Model a -> Model a
-clear m = { m
- | value = ""
- , results = []
- , sel = ""
- , loading = False
- }
-
-
-type Msg a
- = Noop
- | Focus
- | Blur
- | Pos (Result Dom.Error Dom.Element)
- | Input String
- | Search Int
- | Key String
- | Sel String
- | Enter a
- | Results String Api.Response
-
-
-select : Config m a -> Int -> Model a -> Model a
-select cfg offset model =
- let
- get n = List.drop n model.results |> List.head
- count = List.length model.results
- find (n,i) = if cfg.source.key i == model.sel then Just n else Nothing
- curidx = List.indexedMap (\a b -> (a,b)) model.results |> List.filterMap find |> List.head
- nextidx = (Maybe.withDefault -1 curidx) + offset
- nextsel = if nextidx < 0 then 0 else if nextidx >= count then count-1 else nextidx
- in
- { model | sel = Maybe.withDefault "" <| Maybe.map cfg.source.key <| get nextsel }
-
-
-update : Config m a -> Msg a -> Model a -> (Model a, Cmd m, Maybe a)
-update cfg msg model =
- let
- mod m = (m, Cmd.none, Nothing)
- -- Ugly hack: blur and focus the input on enter. This does two things:
- -- 1. If the user clicked on an entry (resulting in the 'Enter' message),
- -- then this will cause the input to be focussed again. This is
- -- convenient when adding multiple entries.
- -- 2. If, as a result of the enter key ('Key Enter' message), the input box
- -- position was moved (likely, because the input box is usually below
- -- the data being added), then this blur + focus causes the 'Focus'
- -- message to be triggered again, updating the position of the dropdown
- -- div. Without this hack the div positioning will be incorrect.
- -- (This hack does rely on the view being updated before these tasks
- -- are executed - but the Dom package seems to guarantee this)
- refocus = Dom.blur cfg.id
- |> Task.andThen (always (Dom.focus cfg.id))
- |> Task.attempt (always (cfg.wrap Noop))
- in
- case msg of
- Noop -> mod model
- Blur -> mod { model | position = Nothing }
- Focus -> ({ model | loading = False }, Task.attempt (cfg.wrap << Pos) (Dom.getElement cfg.id), Nothing)
- Pos (Ok p) -> mod { model | position = Just p }
- Pos _ -> mod model
- Sel s -> mod { model | sel = s }
- Enter r -> (model, refocus, Just r)
-
- Key "Enter" -> (model, refocus, List.filter (\i -> cfg.source.key i == model.sel) model.results |> List.head)
- Key "ArrowUp" -> mod <| select cfg -1 model
- Key "ArrowDown" -> mod <| select cfg 1 model
- Key _ -> mod model
-
- Input s ->
- if s == ""
- then mod { model | value = s, loading = False, results = [] }
- else ( { model | value = s, loading = True, wait = model.wait + 1 }
- , Task.perform (always <| cfg.wrap <| Search <| model.wait + 1) (Process.sleep 500)
- , Nothing )
-
- Search i ->
- if model.value == "" || model.wait /= i
- then mod model
- else ( model
- , Api.post cfg.source.path (JE.object [("search", JE.string model.value)]) (cfg.wrap << Results model.value)
- , Nothing )
-
- Results s r -> mod <|
- if s == model.value
- then { model | loading = False, results = cfg.source.decode r |> Maybe.withDefault [] }
- else model -- Discard stale results
-
-
-view : Config m a -> Model a -> List (Attribute m) -> List (Html m)
-view cfg model attrs =
- let
- input =
- inputText cfg.id model.value (cfg.wrap << Input)
- [ onFocus <| cfg.wrap Focus
- , onBlur <| cfg.wrap Blur
- , custom "keydown" <| JD.map (\c ->
- if c == "Enter" || c == "ArrowUp" || c == "ArrowDown"
- then { preventDefault = True, stopPropagation = True, message = cfg.wrap (Key c) }
- else { preventDefault = False, stopPropagation = False, message = cfg.wrap (Key c) }
- ) <| JD.field "key" JD.string
- ]
-
- inputDiv = div
- (classList [("form-control-wrap",True), ("form-control-wrap--loading",model.loading)] :: attrs)
- [ input ]
-
- msg = [("",
- if List.isEmpty model.results
- then b [] [text "No results"]
- else text ""
- )]
-
- box p =
- Keyed.node "div"
- [ style "top" <| String.fromFloat (p.element.y + p.element.height) ++ "px"
- , style "left" <| String.fromFloat p.element.x ++ "px"
- , style "width" <| String.fromFloat p.element.width ++ "px"
- , class "dropdown-menu dropdown-menu--open"
- ] <| msg ++ List.map item model.results
-
- item i =
- ( cfg.source.key i
- , a
- [ href "#"
- , classList [("dropdown-menu__item", True), ("dropdown-menu__item--active", cfg.source.key i == model.sel) ]
- , onMouseOver <| cfg.wrap <| Sel <| cfg.source.key i
- , onMouseDown <| cfg.wrap <| Enter i
- ] <| cfg.source.view i
- )
-
- in
- [ inputDiv
- , case model.position of
- Nothing -> text ""
- Just p ->
- if model.value == "" || (model.loading && List.isEmpty model.results)
- then text ""
- else box p
- ]
diff --git a/elm3/Lib/Editsum.elm b/elm3/Lib/Editsum.elm
deleted file mode 100644
index 3ddc1506..00000000
--- a/elm3/Lib/Editsum.elm
+++ /dev/null
@@ -1,59 +0,0 @@
--- This module provides an the 'Edit summary' box, including the 'hidden' and
--- 'locked' moderation checkboxes.
-
-module Lib.Editsum exposing (Model, Msg, new, update, view)
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Lib.Html exposing (..)
-
-
-type alias Model =
- { authmod : Bool
- , locked : Bool
- , hidden : Bool
- , editsum : String
- }
-
-
-type Msg
- = Locked Bool
- | Hidden Bool
- | Editsum String
-
-
-new : Model
-new =
- { authmod = False
- , locked = False
- , hidden = False
- , editsum = ""
- }
-
-
-update : Msg -> Model -> Model
-update msg model =
- case msg of
- Locked b -> { model | locked = b }
- Hidden b -> { model | hidden = b }
- Editsum s -> { model | editsum = s }
-
-
-view : Model -> Html Msg
-view model =
- let
- lockhid = cardRow "Mod actions" Nothing <| formGroups
- [ [ label [ class "checkbox" ]
- [ inputCheck "" model.locked Locked
- , text " Locked" ]
- ]
- , [ label [ class "checkbox" ]
- [ inputCheck "" model.hidden Hidden
- , text " Hidden" ]
- ]
- ]
- in card_
- [ lockhid
- , cardRow "Edit summary" (Just "English please!")
- <| formGroup [ inputTextArea "" model.editsum Editsum [rows 4, minlength 2, maxlength 5000, required True] ]
- ]
diff --git a/elm3/Lib/Ffi.elm b/elm3/Lib/Ffi.elm
deleted file mode 100644
index 6c3cbf46..00000000
--- a/elm3/Lib/Ffi.elm
+++ /dev/null
@@ -1,29 +0,0 @@
--- Elm 0.19: "We've removed all Native modules and plugged all XSS vectors,
--- it's now impossible to talk with Javascript other than with ports!"
--- Me: "Oh yeah? I'll just run sed over the generated Javascript!"
-
--- This module is a hack to work around the lack of an FFI (Foreign Function
--- Interface) in Elm. The functions in this module are stubs, their
--- implementations are replaced by the Makefile with calls to
--- window.elmFfi_<name> and the actual implementations are in static/v3/vndb.js.
---
--- Use sparingly, all of this will likely break in future Elm versions.
-module Lib.Ffi exposing (..)
-
-import Html exposing (Attribute)
-import Html.Attributes exposing (title)
-
-
--- This is an "onclick = openLightbox(this)" attribute
-openLightbox : Attribute msg
-openLightbox = title ""
-
-
--- Set the innerHTML attribute of a node
-innerHtml : String -> Attribute msg
-innerHtml = always (title "")
-
-
--- The current year
-curYear : Int
-curYear = 2018
diff --git a/elm3/Lib/Html.elm b/elm3/Lib/Html.elm
deleted file mode 100644
index b811f9fd..00000000
--- a/elm3/Lib/Html.elm
+++ /dev/null
@@ -1,182 +0,0 @@
-module Lib.Html exposing (..)
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Html.Events exposing (..)
-import List
-import Lib.Api as Api
-import Lib.Gen exposing (urlStatic)
-import Lib.Ffi as Ffi
-import Json.Encode as JE
-import String exposing (padLeft)
-
--- Quick short-hand way of creating a form that can be disabled.
--- Usage:
--- form Submit_msg (state == Disabled) [contents]
-form_ : msg -> Bool -> List (Html msg) -> Html msg
-form_ sub dis cont = Html.form [ onSubmit sub ]
- [ fieldset [disabled dis] cont ]
-
-
--- Submit button with loading indicator and error message display
--- TODO: This use of pull-right is ugly.
-submitButton : String -> Api.State -> Bool -> Bool -> Html m
-submitButton val state valid load = div []
- [ input [ type_ "submit", class "btn pull-right", tabindex 10, value val, disabled (state == Api.Loading || not valid || load) ] []
- , case state of
- Api.Error r -> div [class "invalid-feedback pull-right" ] [ text <| Api.showResponse r ]
- _ -> if valid
- then text ""
- else div [class "invalid-feedback pull-right" ] [ text "The form contains errors, please fix these before submitting. " ]
- , if state == Api.Loading || load
- then div [ class "spinner spinner--md pull-right" ] []
- else text ""
- ]
-
-
-inputSelect : List (Attribute m) -> String -> List (String, String) -> Html m
-inputSelect attrs sel lst =
- let opt (id, name) = option [ value id, selected (id == sel) ] [ text name ]
- in select ([class "form-control", tabindex 10] ++ attrs) <| List.map opt lst
-
-
-inputText : String -> String -> (String -> m) -> List (Attribute m) -> Html m
-inputText nam val onch attrs = input (
- [ type_ "text"
- , class "form-control"
- , tabindex 10
- , value val
- , onInput onch
- ]
- ++ attrs
- ++ (if nam == "" then [] else [ id nam, name nam ])
- ) []
-
-inputTextArea : String -> String -> (String -> m) -> List (Attribute m) -> Html m
-inputTextArea nam val onch attrs = textarea (
- [ class "form-control"
- , tabindex 10
- , onInput onch
- ]
- ++ attrs
- ++ (if nam == "" then [] else [ id nam, name nam ])
- ) [ text val ]
-
-inputCheck : String -> Bool -> (Bool -> m) -> Html m
-inputCheck nam val onch = input (
- [ type_ "checkbox"
- , tabindex 10
- , onCheck onch
- , checked val
- ]
- ++ (if nam == "" then [] else [ id nam, name nam ])
- ) []
-
-inputRadio : String -> Bool -> (Bool -> m) -> Html m
-inputRadio nam val onch = input (
- [ type_ "radio"
- , tabindex 10
- , onCheck onch
- , checked val
- ]
- ++ (if nam == "" then [] else [ name nam ])
- ) []
-
--- Generate a card with: Id, Title, [Header stuff], [Sections]
--- TODO: Also abstract "small-card"s (many of the User/ things) into this
-card : String -> String -> List (Html m) -> List (Html m) -> Html m
-card i t h sections = div
- ([class "card"] ++ if i == "" then [] else [id i])
- <|
- [ div [class "card__header"] ([ div [class "card__title"] [text t] ] ++ h)
- ] ++ List.map (\c -> div [class "card__section"] [c]) sections
-
--- Card without header
-card_ : List (Html m) -> Html m
-card_ c = div [class "card"] [ div [class "card__body"] c ]
-
--- Generate a 2-column row for use within a card section: Title, Subtitle, Content
-cardRow : String -> Maybe String -> List (Html m) -> Html m
-cardRow t s c = div [class "row"]
- [ div [class "col-md col-md--1 card__form-section-left"]
- [ div [class "card__form-section-title"] [text t]
- , case s of
- Just n -> div [class "card__form-section-subtitle"] [text n]
- Nothing -> text ""
- ]
- , div [class "col-md col-md--2"] c
- ]
-
-formGroup : List (Html m) -> List (Html m)
-formGroup c = [div [class "form-group"] c]
-
-formGroups : List (List (Html m)) -> List (Html m)
-formGroups groups = List.map (\c -> div [class "form-group"] c) groups
-
-
-removeButton : m -> Html m
-removeButton cmd = button [type_ "button", class "btn", tabindex 10, onClick cmd]
- [ span [class "d-none d-sm-inline"] [text "x"]
- , span [class "d-sm-none"] [text "Remove"]
- ]
-
-
-
-editList : List (Html m) -> List (Html m)
-editList ct =
- if List.isEmpty ct
- then []
- else [ div [class "editable-list editable-list--sm"] ct ]
-
-editListRow : String -> List (Html m) -> Html m
-editListRow cl ct = div [class ("editable-list__row row row--compact " ++ cl)] ct
-
-editListField : Int -> String -> List (Html m) -> Html m
-editListField sm cl ct = div
- [ classList <|
- [ ("editable-list__field", True)
- , ("col-sm", True )
- , ("col-sm--auto", sm == 0)
- , ("col-sm--1", sm == 1)
- , ("col-sm--2", sm == 2)
- , ("col-sm--3", sm == 3)
- , (cl, cl /= "")
- ]
- ] ct
-
-
--- Special arguments,
--- id == -1 -> spinner
--- id == 0 -> camera-alt.svg
-dbImg : String -> Int -> List (Attribute m) -> Maybe { id: String, width: Int, height: Int } -> Html m
-dbImg dir id attrs full =
- if id == 0 then
- div (class "vn-image-placeholder img--rounded" :: attrs)
- [ div [ class "vn-image-placeholder__icon" ]
- [ img [ src (urlStatic ++ "/v3/camera-alt.svg"), class "svg-icon" ] [] ]
- ]
- else if id == -1 then
- div (class "vn-image-placeholder img--rounded" :: attrs)
- [ div [ class "vn-image-placeholder__icon" ]
- [ div [ class "spinner spinner--md" ] [] ]
- ]
- else
- let
- url d = urlStatic ++ "/" ++ d ++ "/" ++ (padLeft 2 '0' (String.fromInt (modBy 100 id))) ++ "/" ++ (String.fromInt id) ++ ".jpg"
- i = img [src (url dir), class "img--fit img--rounded" ] []
- fdir = if dir == "st" then "sf" else dir
- in case full of
- Nothing -> i
- Just f -> a
- [ href (url fdir)
- , Ffi.openLightbox
- , attribute "data-lightbox-id" f.id
- , attribute "data-lightbox-nfo" <| JE.encode 0 <| JE.object [("width", JE.int f.width), ("height", JE.int f.height)]
- ] [ i ]
-
-
-iconLanguage : String -> Html msg
-iconLanguage lang = span [ class "lang-badge" ] [ text lang ]
-
-iconPlatform : String -> Html msg
-iconPlatform plat = img [ class "svg-icon", src (urlStatic ++ "/v3/windows.svg"), title "Windows" ] []
diff --git a/elm3/Lib/RDate.elm b/elm3/Lib/RDate.elm
deleted file mode 100644
index 397f7243..00000000
--- a/elm3/Lib/RDate.elm
+++ /dev/null
@@ -1,84 +0,0 @@
--- Utility module and UI widget for handling release dates.
---
--- Release dates are integers with the following format: 0 or yyyymmdd
--- Special values
--- 0 -> unknown
--- 99999999 -> TBA
--- yyyy9999 -> year known, month & day unknown
--- yyyymm99 -> year & month known, day unknown
---
--- I'm not a big fan of the UI widget. It's functional, but could be much more
--- convenient and intuitive.
-module Lib.RDate exposing (..)
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Html.Events exposing (..)
-import Date
-import Lib.Html exposing (..)
-import Lib.Ffi exposing (curYear)
-
-
-type alias RDate = Int
-
-type alias RDateComp =
- { y : Int
- , m : Int
- , d : Int
- }
-
-
-expand : RDate -> RDateComp
-expand r =
- { y = r // 10000
- , m = modBy 100 (r // 100)
- , d = modBy 100 r
- }
-
-
-compact : RDateComp -> RDate
-compact r = r.y * 10000 + r.m * 100 + r.d
-
-
-normalize : RDateComp -> RDateComp
-normalize r =
- if r.y == 0 then { y = 0, m = 0, d = 0 }
- else if r.y == 9999 then { y = 9999, m = 99, d = 99 }
- else if r.m == 99 then { y = r.y, m = 99, d = 99 }
- else r
-
-
-type Msg
- = Year String
- | Month String
- | Day String
-
-
-update : Msg -> RDate -> RDate
-update msg ro =
- let r = expand ro
- in compact <| normalize <| case msg of
- Year s -> { r | y = Maybe.withDefault r.y <| String.toInt s }
- Month s -> { r | m = Maybe.withDefault r.m <| String.toInt s }
- Day s -> { r | d = Maybe.withDefault r.d <| String.toInt s }
-
-
-view : RDate -> Bool -> Html Msg
-view ro permitUnknown =
- let r = expand ro
- range s = List.range s >> List.map (\n -> (String.fromInt n, String.fromInt n))
- yl = (if permitUnknown then [("0", "Unknown")] else [])
- ++ List.reverse (range 1980 (curYear + 5))
- ++ [("9999", "TBA")]
- ml = ("99", "- month -") :: (range 1 12)
- maxDay = Date.fromCalendarDate r.y (Date.numberToMonth r.m) 1 |> Date.add Date.Months 1 |> Date.add Date.Days -1 |> Date.day
- dl = ("99", "- day -") :: (range 1 maxDay)
- in div []
- [ inputSelect [class "form-control--inline", onInput Year] (String.fromInt r.y) yl
- , if r.y == 0 || r.y == 9999
- then text ""
- else inputSelect [class "form-control--inline", onInput Month] (String.fromInt r.m) ml
- , if r.m == 0 || r.m == 99
- then text ""
- else inputSelect [class "form-control--inline", onInput Day] (String.fromInt r.d) dl
- ]
diff --git a/elm3/Lib/Util.elm b/elm3/Lib/Util.elm
deleted file mode 100644
index f6b39188..00000000
--- a/elm3/Lib/Util.elm
+++ /dev/null
@@ -1,76 +0,0 @@
-module Lib.Util exposing (..)
-
-import Char
-import Dict
-
--- Delete an element from a List
-delidx : Int -> List a -> List a
-delidx n l = List.take n l ++ List.drop (n+1) l
-
-
--- Modify an element in a List
-modidx : Int -> (a -> a) -> List a -> List a
-modidx n f = List.indexedMap (\i e -> if i == n then f e else e)
-
-
-isJust : Maybe a -> Bool
-isJust m = case m of
- Just _ -> True
- _ -> False
-
-
--- Split by newline, trim whitespace and remove empty lines
-splitLn : String -> List String
-splitLn = String.lines >> List.map String.trim >> List.filter ((/=)"")
-
--- Returns true if the list contains duplicates
-hasDuplicates : List comparable -> Bool
-hasDuplicates l =
- let
- step e acc =
- case acc of
- Nothing -> Nothing
- Just m -> if Dict.member e m then Nothing else Just (Dict.insert e True m)
- in
- case List.foldr step (Just Dict.empty) l of
- Nothing -> True
- Just _ -> False
-
-
--- Similar to perl's ucfirst() (not terribly efficient)
-toUpperFirst : String -> String
-toUpperFirst s = String.toList s |> List.indexedMap (\i c -> if i == 0 then Char.toUpper c else c) |> String.fromList
-
-
--- Haskell's 'lookup' - find an entry in an association list
-lookup : a -> List (a,b) -> Maybe b
-lookup n l = List.filter (\(a,_) -> a == n) l |> List.head |> Maybe.map Tuple.second
-
-
-formatGtin : Int -> String
-formatGtin n = if n == 0 then "" else String.fromInt n |> String.padLeft 12 '0'
-
-
--- Based on VNDBUtil::gtintype()
-validateGtin : String -> Maybe Int
-validateGtin =
- let check = String.fromInt
- >> String.reverse
- >> String.toList
- >> List.indexedMap (\i c -> (Char.toCode c - Char.toCode '0') * if modBy 2 i == 0 then 1 else 3)
- >> List.sum
- inval n =
- n < 1000000000
- || (n >= 200000000000 && n < 600000000000)
- || (n >= 2000000000000 && n < 3000000000000)
- || n >= 9770000000000
- || modBy 10 (check n) /= 0
- in String.filter Char.isDigit >> String.toInt >> Maybe.andThen (\n -> if inval n then Nothing else Just n)
-
-
-spoilLevels : List (String, String)
-spoilLevels =
- [ ("0", "No spoiler")
- , ("1", "Minor spoiler")
- , ("2", "Major spoiler")
- ]
diff --git a/elm3/Lightbox.elm b/elm3/Lightbox.elm
deleted file mode 100644
index db19fc78..00000000
--- a/elm3/Lightbox.elm
+++ /dev/null
@@ -1,178 +0,0 @@
-port module Lightbox exposing (main)
-
--- TODO: Display quick-select thumbnails below the image if there's enough room?
--- TODO: The first image in a gallery is not aligned properly
--- TODO: Indicate which images are NSFW
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Html.Events exposing (..)
-import Array
-import Task
-import List
-import Browser
-import Browser.Events as EV
-import Browser.Dom as DOM
-import Json.Decode as JD
-import Lib.Html exposing (..)
-
-
-main : Program () (Maybe Model) Msg
-main = Browser.element
- { init = always (Nothing, Cmd.none)
- , view = view
- , update = update
- , subscriptions = \m ->
- if m == Nothing
- then open Open
- else Sub.batch
- [ EV.onResize Resize
- , EV.onKeyDown <| JD.map Keydown <| JD.field "key" JD.string
- , preloaded Preloaded
- ]
- }
-
-port close : Bool -> Cmd msg
-port open : (Model -> msg) -> Sub msg
-port preload : String -> Cmd msg
-port preloaded : (String -> msg) -> Sub msg
-
-type alias Release =
- { id : Int
- , title : String
- , lang : List String
- , plat : List String
- }
-
-type alias Image =
- { thumb : String
- , full : String
- , width : Int
- , height : Int
- , load : Bool
- , rel : Maybe Release
- }
-
-type alias Model =
- { images : Array.Array Image
- , current : Int
- , width : Int
- , height : Int
- }
-
-type Msg
- = Noop
- | Next
- | Prev
- | Open Model
- | Close
- | Resize Int Int
- | Viewport DOM.Viewport
- | Preloaded String
- | Keydown String
-
-
-setPreload : Model -> Cmd Msg
-setPreload model =
- let cmd n =
- case Array.get (model.current+n) model.images of
- Nothing -> Cmd.none
- Just i -> if i.load then Cmd.none else preload i.full
- in if cmd 0 /= Cmd.none then cmd 0 else Cmd.batch [cmd -1, cmd 1]
-
-
-update_ : Msg -> Model -> (Model, Cmd Msg)
-update_ msg model =
- let move n =
- case Array.get (model.current+n) model.images of
- Nothing -> (model, Cmd.none)
- Just i -> let m = { model | current = model.current+n } in (m, setPreload m)
- in
- case msg of
- Noop -> (model, Cmd.none)
- Next -> move 1
- Prev -> move -1
- Keydown "ArrowLeft" -> move -1
- Keydown "ArrowRight" -> move 1
- Keydown _ -> (model, Cmd.none)
- Resize width height -> ({ model | width = width, height = height }, Cmd.none)
- Viewport v -> ({ model | width = round v.viewport.width, height = round v.viewport.height }, Cmd.none)
- Preloaded url ->
- let m = { model | images = Array.map (\img -> if img.full == url then { img | load = True } else img) model.images }
- in (m, setPreload m)
- _ -> (model, Cmd.none)
-
-
-update : Msg -> Maybe Model -> (Maybe Model, Cmd Msg)
-update msg model =
- case (msg, model) of
- (Open m , _) -> ( Just m
- , Cmd.batch [setPreload m, Task.perform Viewport DOM.getViewport]
- )
- (Close , _) -> (Nothing, close True)
- (Keydown "Escape", _) -> (Nothing, close True)
- (_ , Just m) -> let (newm, cmd) = update_ msg m in (Just newm, cmd)
- _ -> (model, Cmd.none)
-
-
-
-view_ : Model -> Html Msg
-view_ model =
- let
- -- 'onClick' with stopPropagation and preventDefault
- onClickN action = custom "click" (JD.succeed { message = action, stopPropagation = True, preventDefault = True})
- -- 'onClick' with stopPropagation
- onClickP action = custom "click" (JD.succeed { message = action, stopPropagation = True, preventDefault = False})
-
- -- Maximum image dimensions
- awidth = toFloat model.width * 0.84
- aheight = toFloat model.height - 80
-
- full_img action position i =
- -- Scale image down to fit inside awidth/aheight
- let swidth = awidth / toFloat i.width
- sheight = aheight / toFloat i.height
- scale = Basics.min 1 <| if swidth < sheight then swidth else sheight
- iwidth = round <| scale * toFloat i.width
- iheight = round <| scale * toFloat i.height
- cwidth = style "width" <| String.fromInt iwidth ++ "px"
- cheight = style "height" <| String.fromInt iheight ++ "px"
- imgsrc = if i.load then i.full else i.thumb
- in
- a [ href "#", onClickN action, cheight
- , class <| "lightbox__image lightbox__image-" ++ position ]
- [ img [ class "lightbox__img", src imgsrc, cwidth, cheight ] [] ]
-
- full offset action position =
- case Array.get (model.current + offset) model.images of
- Nothing -> text ""
- Just i -> full_img action position i
-
- meta img = div [ class "lightbox__meta", onClickP Noop ] <|
- [ a [ href img.full, class "lightbox__dims" ] [ text <| String.fromInt img.width ++ "x" ++ String.fromInt img.height ]
- ] ++ relMeta img.rel
-
- relMeta r = case r of
- Nothing -> []
- Just rel ->
- (List.map iconPlatform rel.plat)
- ++ (List.map iconLanguage rel.lang)
- ++ [ a [ href ("/r" ++ String.fromInt rel.id) ] [ text rel.title ] ]
-
- container img = div [ class "lightbox", onClick Close ]
- [ a [ href "#", onClickN Close, class "lightbox__close" ] []
- , full -1 Prev "left"
- , full 0 Close "current"
- , full 1 Next "right"
- , meta img
- ]
-
- in case Array.get model.current model.images of
- Just img -> container img
- Nothing -> text ""
-
-
-view : (Maybe Model) -> Html Msg
-view m = case m of
- Just mod -> view_ mod
- Nothing -> text ""
diff --git a/elm3/ProdEdit/General.elm b/elm3/ProdEdit/General.elm
deleted file mode 100644
index 19ca79b7..00000000
--- a/elm3/ProdEdit/General.elm
+++ /dev/null
@@ -1,78 +0,0 @@
-module ProdEdit.General exposing (..)
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Html.Events exposing (..)
-import Lib.Html exposing (..)
-import Lib.Gen exposing (languages, weburlPattern, producerTypes, ProdEdit)
-import Lib.Util exposing (..)
-
-
-type alias Model =
- { desc : String
- , l_wp : String
- , lang : String
- , ptype : String
- , website : String
- }
-
-
-init : ProdEdit -> Model
-init d =
- { desc = d.desc
- , l_wp = d.l_wp
- , lang = d.lang
- , ptype = d.ptype
- , website = d.website
- }
-
-
-new : Model
-new =
- { desc = ""
- , l_wp = ""
- , lang = "ja"
- , ptype = "co"
- , website = ""
- }
-
-
-type Msg
- = Desc String
- | LWP String
- | Lang String
- | PType String
- | Website String
-
-
-update : Msg -> Model -> Model
-update msg model =
- case msg of
- Desc s -> { model | desc = s }
- LWP s -> { model | l_wp = s }
- Lang s -> { model | lang = s }
- PType s -> { model | ptype = s }
- Website s -> { model | website = s }
-
-
-view : Model -> (Msg -> a) -> List (Html a) -> Html a
-view model wrap names = card "general" "General info" [] <|
- names ++ List.map (Html.map wrap)
- [ cardRow "Meta" Nothing <| formGroups
- [ [ label [for "ptype"] [ text "Type" ]
- , inputSelect [id "ptype", name "ptype", onInput PType] model.ptype producerTypes
- ]
- , [ label [for "lang"] [ text "Primary language" ]
- , inputSelect [id "lang", name "lang", onInput Lang] model.lang languages
- ]
- , [ label [for "website"] [ text "Official Website" ]
- , inputText "website" model.website Website [pattern weburlPattern]
- ]
- , [ label [] [ text "Wikipedia" ]
- , p [] [ text "https://en.wikipedia.org/wiki/", inputText "l_wp" model.l_wp LWP [class "form-control--inline", maxlength 100] ]
- ]
- ]
-
- , cardRow "Description" (Just "English please!") <| formGroup
- [ inputTextArea "desc" model.desc Desc [rows 8] ]
- ]
diff --git a/elm3/ProdEdit/Main.elm b/elm3/ProdEdit/Main.elm
deleted file mode 100644
index 06328f53..00000000
--- a/elm3/ProdEdit/Main.elm
+++ /dev/null
@@ -1,158 +0,0 @@
-module ProdEdit.Main exposing (Model, Msg, main, new, view, update)
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Json.Encode as JE
-import Browser
-import Browser.Navigation exposing (load)
-import Lib.Util exposing (splitLn)
-import Lib.Html exposing (..)
-import Lib.Gen as Gen
-import Lib.Api as Api
-import Lib.Editsum as Editsum
-import ProdEdit.Names as Names
-import ProdEdit.General as General
-import ProdEdit.Relations as Rel
-
-
-main : Program Gen.ProdEdit Model Msg
-main = Browser.element
- { init = \e -> (init e, Cmd.none)
- , view = view
- , update = update
- , subscriptions = always Sub.none
- }
-
-
-type alias Model =
- { state : Api.State
- , new : Bool
- , editsum : Editsum.Model
- , names : Names.Model
- , general : General.Model
- , relations : Rel.Model
- , id : Maybe Int
- , dupProds : List Gen.ApiProducerResult
- }
-
-
-init : Gen.ProdEdit -> Model
-init d =
- { state = Api.Normal
- , new = False
- , editsum = { authmod = d.authmod, editsum = d.editsum, locked = d.locked, hidden = d.hidden }
- , names = Names.init d
- , general = General.init d
- , relations = Rel.init d.relations
- , id = d.id
- , dupProds = []
- }
-
-
-new : Model
-new =
- { state = Api.Normal
- , new = True
- , editsum = Editsum.new
- , names = Names.new
- , general = General.new
- , relations = Rel.init []
- , id = Nothing
- , dupProds = []
- }
-
-
-encode : Model -> Gen.ProdEditSend
-encode model =
- { editsum = model.editsum.editsum
- , hidden = model.editsum.hidden
- , locked = model.editsum.locked
- , name = model.names.name
- , original = model.names.original
- , alias = model.names.alias
- , desc = model.general.desc
- , lang = model.general.lang
- , ptype = model.general.ptype
- , l_wp = model.general.l_wp
- , website = model.general.website
- , relations = List.map (\e -> { pid = e.pid, relation = e.relation }) model.relations.relations
- }
-
-
-type Msg
- = Editsum Editsum.Msg
- | Submit
- | Submitted Api.Response
- | Names Names.Msg
- | General General.Msg
- | Relations Rel.Msg
- | CheckDup
- | RecvDup Api.Response
-
-
-update : Msg -> Model -> (Model, Cmd Msg)
-update msg model =
- case msg of
- Names m -> ({ model | names = Names.update m model.names, dupProds = [] }, Cmd.none)
- General m -> ({ model | general = General.update m model.general }, Cmd.none)
- Editsum m -> ({ model | editsum = Editsum.update m model.editsum }, Cmd.none)
- Relations m -> let (nm, c) = Rel.update m model.relations in ({ model | relations = nm }, Cmd.map Relations c)
-
- Submit ->
- let
- path =
- case model.id of
- Just id -> "/p" ++ String.fromInt id ++ "/edit"
- Nothing -> "/p/add"
- body = Gen.prodeditSendEncode (encode model)
- in ({ model | state = Api.Loading }, Api.post path body Submitted)
-
- Submitted (Gen.Changed id rev) -> (model, load <| "/p" ++ String.fromInt id ++ "." ++ String.fromInt rev)
- Submitted r -> ({ model | state = Api.Error r }, Cmd.none)
-
- CheckDup ->
- let body = JE.object
- [ ("search", JE.list JE.string <| List.filter ((/=)"") <| model.names.name :: model.names.original :: model.names.aliasList)
- , ("hidden", JE.bool True) ]
- in
- if List.isEmpty model.dupProds
- then ({ model | state = Api.Loading }, Api.post "/js/producer.json" body RecvDup)
- else ({ model | new = False }, Cmd.none)
-
- RecvDup (Gen.ProducerResult dup) ->
- ({ model | state = Api.Normal, dupProds = dup, new = not (List.isEmpty dup) }, Cmd.none)
- RecvDup r -> ({ model | state = Api.Error r }, Cmd.none)
-
-
-
-isValid : Model -> Bool
-isValid model = not
- ( model.names.aliasDuplicates
- || model.relations.duplicates
- )
-
-
-view : Model -> Html Msg
-view model =
- if model.new
- then form_ CheckDup (model.state == Api.Loading)
- [ card "new" "Add a new producer" []
- <| List.map (Html.map Names) <| Names.view model.names
- , if List.isEmpty model.dupProds
- then text ""
- else card "dup" "Possible duplicates" [ div [ class "card__subheading" ] [ text "Please check the list below for possible duplicates." ] ]
- [ cardRow "" Nothing <| formGroup [ div [ class "form-group__help" ] [
- ul [] <| List.map (\e ->
- li [] [ a [ href <| "/p" ++ String.fromInt e.id, title e.original, target "_black" ] [ text e.name ]
- , text <| if e.hidden then " (deleted)" else "" ]
- ) model.dupProds
- ] ] ]
- , submitButton "Continue" model.state (isValid model) False
- ]
-
- else form_ Submit (model.state == Api.Loading)
- [ General.view model.general General <| List.map (Html.map Names) <| Names.view model.names
- , Html.map Relations <| Rel.view model.relations
- , Html.map Editsum <| Editsum.view model.editsum
- , submitButton "Submit" model.state (isValid model) False
- ]
diff --git a/elm3/ProdEdit/Names.elm b/elm3/ProdEdit/Names.elm
deleted file mode 100644
index e28f4916..00000000
--- a/elm3/ProdEdit/Names.elm
+++ /dev/null
@@ -1,81 +0,0 @@
-module ProdEdit.Names exposing (..)
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Dict
-import Lib.Html exposing (..)
-import Lib.Gen exposing (ProdEdit)
-import Lib.Util exposing (..)
-
-
-type alias Model =
- { name : String
- , original : String
- , alias : String
- , aliasList : List String
- , aliasDuplicates : Bool
- }
-
-
-init : ProdEdit -> Model
-init d =
- { name = d.name
- , original = d.original
- , alias = d.alias
- , aliasList = splitLn d.alias
- , aliasDuplicates = False
- }
-
-
-new : Model
-new =
- { name = ""
- , original = ""
- , alias = ""
- , aliasList = []
- , aliasDuplicates = False
- }
-
-
-type Msg
- = Name String
- | Original String
- | Alias String
-
-
-update : Msg -> Model -> Model
-update msg model =
- case msg of
- Name s -> { model | name = s }
- Original s -> { model | original = s }
- Alias s ->
- { model
- | alias = s
- , aliasList = splitLn s
- , aliasDuplicates = hasDuplicates (model.name :: model.original :: splitLn s)
- }
-
-
-view : Model -> List (Html Msg)
-view model =
- [ cardRow "Name" Nothing <| formGroups
- [ [ label [for "name"] [text "Name (romaji)"]
- , inputText "name" model.name Name [required True, maxlength 200]
- ]
- , [ label [for "original"] [text "Original"]
- , inputText "original" model.original Original [maxlength 200]
- , div [class "form-group__help"] [text "The original name of this producer, leave blank if it already is in the Latin alphabet."]
- ]
- ]
- , cardRow "Aliases" Nothing <| formGroup
- [ inputTextArea "aliases" model.alias Alias
- [ rows 4, maxlength 500
- , classList [("is-invalid", model.aliasDuplicates)]
- ]
- , if model.aliasDuplicates
- then div [class "invalid-feedback"]
- [ text "There are duplicate aliases." ]
- else text ""
- , div [class "form-group__help"] [ text "(Un)official aliases, separated by a newline." ]
- ]
- ]
diff --git a/elm3/ProdEdit/New.elm b/elm3/ProdEdit/New.elm
deleted file mode 100644
index c1df2ac4..00000000
--- a/elm3/ProdEdit/New.elm
+++ /dev/null
@@ -1,12 +0,0 @@
-module ProdEdit.New exposing (main)
-
-import Browser
-import ProdEdit.Main as Main
-
-main : Program () Main.Model Main.Msg
-main = Browser.element
- { init = always (Main.new, Cmd.none)
- , view = Main.view
- , update = Main.update
- , subscriptions = always Sub.none
- }
diff --git a/elm3/ProdEdit/Relations.elm b/elm3/ProdEdit/Relations.elm
deleted file mode 100644
index b1887684..00000000
--- a/elm3/ProdEdit/Relations.elm
+++ /dev/null
@@ -1,79 +0,0 @@
-module ProdEdit.Relations exposing (Model, Msg, init, update, view)
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Html.Events exposing (..)
-import Lib.Html exposing (..)
-import Lib.Gen as Gen
-import Lib.Util exposing (..)
-import Lib.Autocomplete as A
-
-
-type alias Model =
- { relations : List Gen.ProdEditRelations
- , search : A.Model Gen.ApiProducerResult
- , duplicates : Bool
- }
-
-
-init : List Gen.ProdEditRelations -> Model
-init l =
- { relations = l
- , search = A.init
- , duplicates = False
- }
-
-
-type Msg
- = Del Int
- | Rel Int String
- | Search (A.Msg Gen.ApiProducerResult)
-
-
-searchConfig : A.Config Msg Gen.ApiProducerResult
-searchConfig = { wrap = Search, id = "add-relation", source = A.producerSource }
-
-
-validate : Model -> Model
-validate model = { model | duplicates = hasDuplicates <| List.map .pid model.relations }
-
-
-update : Msg -> Model -> (Model, Cmd Msg)
-update msg model =
- case msg of
- Del i -> (validate { model | relations = delidx i model.relations }, Cmd.none)
- Rel i s -> (validate { model | relations = modidx i (\e -> { e | relation = s }) model.relations }, Cmd.none)
- Search m ->
- let (nm, c, res) = A.update searchConfig m model.search
- in case res of
- Nothing -> ({ model | search = nm }, c)
- Just r ->
- let
- rel = List.head Gen.producerRelations |> Maybe.map Tuple.first |> Maybe.withDefault ""
- nrow = { pid = r.id, relation = rel, name = r.name }
- in (validate { model | search = A.clear nm, relations = model.relations ++ [nrow] }, c)
-
-
-view : Model -> Html Msg
-view model =
- let
- entry n e = editListRow "row--ai-center"
- [ editListField 1 "single-line"
- [ a [href <| "/p" ++ String.fromInt e.pid, title e.name, target "_blank" ] [text e.name ] ]
- , editListField 1 ""
- [ inputSelect [onInput (Rel n)] e.relation Gen.producerRelations ]
- , editListField 0 "" [ removeButton (Del n) ]
- ]
-
- in card "relations" "Relations" [] <|
- editList (List.indexedMap entry model.relations)
- ++ formGroups (
- (if model.duplicates
- then [ [ div [ class "invalid-feedback" ]
- [ text "The list contains duplicates. Make sure that the same producer is not listed multiple times." ] ] ]
- else []
- ) ++
- [ label [for "add-relation"] [text "Add relation"]
- :: A.view searchConfig model.search [placeholder "Producer...", style "max-width" "400px"]
- ]
- )
diff --git a/elm3/RelEdit/General.elm b/elm3/RelEdit/General.elm
deleted file mode 100644
index 698c9f99..00000000
--- a/elm3/RelEdit/General.elm
+++ /dev/null
@@ -1,272 +0,0 @@
-module RelEdit.General exposing (..)
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Html.Events exposing (..)
-import Lib.Html exposing (..)
-import Lib.Gen exposing (..)
-import Lib.Util exposing (..)
-import Lib.RDate as RDate
-
-
-type alias Model =
- { aniEro : Int
- , aniStory : Int
- , catalog : String
- , doujin : Bool
- , freeware : Bool
- , gtinInput : String
- , gtinVal : Maybe Int
- , lang : List { lang : String }
- , media : List { medium : String, qty : Int }
- , minage : Maybe Int
- , notes : String
- , original : String
- , patch : Bool
- , platforms : List { platform : String }
- , released : RDate.RDate
- , resolution : String
- , rtype : String
- , title : String
- , uncensored : Bool
- , voiced : Int
- , website : String
- }
-
-
-init : RelEdit -> Model
-init d =
- { aniEro = d.ani_ero
- , aniStory = d.ani_story
- , catalog = d.catalog
- , doujin = d.doujin
- , freeware = d.freeware
- , gtinInput = formatGtin d.gtin
- , gtinVal = Just d.gtin
- , lang = d.lang
- , media = d.media
- , minage = d.minage
- , notes = d.notes
- , original = d.original
- , patch = d.patch
- , platforms = d.platforms
- , released = d.released
- , resolution = d.resolution
- , rtype = d.rtype
- , title = d.title
- , uncensored = d.uncensored
- , voiced = d.voiced
- , website = d.website
- }
-
-
-new : String -> String -> Model
-new title orig =
- { aniEro = 0
- , aniStory = 0
- , catalog = ""
- , doujin = False
- , freeware = False
- , gtinInput = ""
- , gtinVal = Just 0
- , lang = [{lang = "ja"}]
- , media = []
- , minage = Nothing
- , notes = ""
- , original = orig
- , patch = False
- , platforms = []
- , released = 99999999
- , resolution = "unknown"
- , rtype = "complete"
- , title = title
- , uncensored = False
- , voiced = 0
- , website = ""
- }
-
-
-type Msg
- = Title String
- | Original String
- | RType String
- | Patch Bool
- | Freeware Bool
- | Doujin Bool
- | LangDel Int
- | LangAdd String
- | Notes String
- | Released RDate.Msg
- | Gtin String
- | Catalog String
- | Website String
- | Minage String
- | Uncensored Bool
- | Resolution String
- | Voiced String
- | AniStory String
- | AniEro String
- | PlatDel Int
- | PlatAdd String
- | MedDel Int
- | MedQty Int String
- | MedAdd String
-
-
-update : Msg -> Model -> Model
-update msg model =
- case msg of
- Title s -> { model | title = s }
- Original s -> { model | original = s }
- RType s -> { model | rtype = s }
- Patch b -> { model | patch = b }
- Freeware b -> { model | freeware = b }
- Doujin b -> { model | doujin = b }
- LangDel n -> { model | lang = delidx n model.lang }
- LangAdd s -> if s == "" then model else { model | lang = model.lang ++ [{ lang = s }] }
- Notes s -> { model | notes = s }
- Released m -> { model | released = RDate.update m model.released }
- Gtin s -> { model | gtinInput= s, gtinVal = if s == "" then Just 0 else validateGtin s }
- Catalog s -> { model | catalog = s }
- Website s -> { model | website = s }
- Minage s -> { model | minage = String.toInt s }
- Uncensored b -> { model | uncensored = b }
- Resolution s -> { model | resolution = s }
- Voiced s -> { model | voiced = Maybe.withDefault model.voiced <| String.toInt s }
- AniStory s -> { model | aniStory = Maybe.withDefault model.aniStory <| String.toInt s }
- AniEro s -> { model | aniEro = Maybe.withDefault model.aniEro <| String.toInt s }
- PlatDel n -> { model | platforms = delidx n model.platforms }
- PlatAdd s -> if s == "" then model else { model | platforms = model.platforms ++ [{ platform = s }] }
- MedDel n -> { model | media = delidx n model.media }
- MedQty i s -> { model | media = modidx i (\e -> { e | qty = Maybe.withDefault 0 (String.toInt s) }) model.media }
- MedAdd s -> if s == "" then model else { model | media = model.media ++ [{ medium = s, qty = 1 }] }
-
-
-general : Model -> Html Msg
-general model = card "general" "General info" []
-
- [ cardRow "Title" Nothing <| formGroups
- [ [ label [for "title"] [text "Title (romaji)"]
- , inputText "title" model.title Title [required True, maxlength 250]
- ]
- , [ label [for "original"] [text "Original"]
- , inputText "original" model.original Original [maxlength 250]
- , div [class "form-group__help"] [text "The original title of this release, leave blank if it already is in the Latin alphabet."]
- ]
- ]
-
- , cardRow "Type" Nothing <| formGroups <|
- [ [ inputSelect [id "type", onInput RType] model.rtype <| List.map (\s -> (s, toUpperFirst s)) releaseTypes ]
- , [ label [class "checkbox"] [ inputCheck "" model.patch Patch , text " This release is a patch to another release" ] ]
- , [ label [class "checkbox"] [ inputCheck "" model.freeware Freeware, text " Freeware (i.e. available at no cost)" ] ]
- ] ++ if model.patch
- then []
- else [ [ label [class "checkbox"] [ inputCheck "" model.doujin Doujin, text " Doujin (self-published, not by a company)" ] ] ]
-
- , cardRow "Languages" Nothing <| formGroups
- [ editList <| List.indexedMap (\n l ->
- editListRow ""
- [ editListField 3 "" [ iconLanguage l.lang, text <| " " ++ (Maybe.withDefault l.lang <| lookup l.lang languages) ]
- , editListField 0 "" [ removeButton (LangDel n) ]
- ]
- ) model.lang
- , [ if List.isEmpty model.lang
- then div [class "invalid-feedback"] [ text "No language selected." ]
- else text ""
- , label [for "addlang"] [ text "Add language" ]
- -- TODO: Move selection back to "" when a new language has been added
- , inputSelect [id "addlang", onInput LangAdd] ""
- <| ("", "-- Add language --")
- :: List.filter (\(n,_) -> not <| List.any (\l -> l.lang == n) model.lang) languages
- ]
- ]
-
- , cardRow "Meta" Nothing <| formGroups <|
- [ [ label [for "released"] [text "Release date"]
- , Html.map Released <| RDate.view model.released False
- , div [class "form-group__help"] [text "Leave month or day blank if they are unknown"]
- ]
- , [ label [for "gtin"] [text "JAN/EAN/UPC"]
- , inputText "gtin" model.gtinInput Gtin [pattern "[0-9]+"]
- , if model.gtinVal == Nothing
- then div [class "invalid-feedback"] [ text "Invalid bar code." ]
- else text ""
- ]
- , [ label [for "catalog"] [text "Catalog number"]
- , inputText "catalog" model.catalog Catalog [maxlength 50]
- ]
- , [ label [for "website"] [text "Official website"]
- , inputText "website" model.website Website [pattern weburlPattern]
- ]
- , [ label [for "minage"] [text "Age rating"]
- , inputSelect [id "minage", onInput Minage] (Maybe.withDefault "" (Maybe.map String.fromInt model.minage)) (List.map (\(a,b) -> (String.fromInt a, b)) minAges)
- ]
- ] ++ if model.minage /= Just 18
- then []
- else [ [ label [class "checkbox"] [ inputCheck "" model.uncensored Uncensored, text " No mosaic or other optical censoring (only check if this release has erotic content)" ] ] ]
-
- , cardRow "Notes" (Just "English please!") <| formGroup
- [ inputTextArea "" model.notes Notes [rows 5, maxlength 10240]
- , div [class "form-group__help"]
- [ text "Miscellaneous notes/comments, information that does not fit in the other fields."
- , text " For example, types of censoring or for which other releases this patch applies."
- ]
- ]
- ]
-
-
-format : Model -> Html Msg
-format model = card "format" "Format" [] <|
-
- (if model.patch then [] else [ cardRow "Technical" Nothing <| formGroups
- [ [ label [for "resolution"] [text "Native screen resolution"]
- , inputSelect [id "resolution", onInput Resolution] model.resolution resolutions
- ]
- , [ label [for "voiced"] [text "Voiced"]
- , inputSelect [id "voiced", onInput Voiced] (String.fromInt model.voiced) <| List.map (\(a,b) -> (String.fromInt a, b)) voiced
- ]
- , [ label [for "ani_story"] [text "Story animation"]
- , inputSelect [id "ani_story", onInput AniStory] (String.fromInt model.aniStory) <| List.map (\(a,b) -> (String.fromInt a, b)) animated
- ]
- , [ label [for "ani_ero"] [text "Ere scene animation"]
- , inputSelect [id "ani_ero", onInput AniEro] (String.fromInt model.aniEro) <| List.map (\(a,b) -> (String.fromInt a, if a == 0 then "Unknown / no ero scenes" else b)) animated
- ]
- ]
- ]) ++
-
- [ cardRow "Platforms" Nothing <| formGroups
- [ editList <| List.indexedMap (\n p ->
- editListRow ""
- [ editListField 3 "" [ iconPlatform p.platform, text <| " " ++ (Maybe.withDefault p.platform <| lookup p.platform platforms) ]
- , editListField 0 "" [ removeButton (PlatDel n) ]
- ]
- ) model.platforms
- , [ label [for "addplat"] [ text "Add platform" ]
- -- TODO: Move selection back to "" when a new platform has been added
- , inputSelect [id "addplat", onInput PlatAdd] ""
- <| ("", "-- Add platform --")
- :: List.filter (\(n,_) -> not <| List.any (\p -> p.platform == n) model.platforms) platforms
- ]
- ]
-
- , cardRow "Media" Nothing <| formGroups
- [ editList <| List.indexedMap (\n m ->
- let md = Maybe.withDefault { qty = False, single = "", plural = "" } <| lookup m.medium Lib.Gen.media
- in editListRow ""
- [ editListField 2 "" [ text md.single ] -- TODO: Add icon
- , editListField 2 ""
- [ if md.qty
- then inputSelect [ onInput (MedQty n) ] (String.fromInt m.qty) <| List.map (\i -> (String.fromInt i, String.fromInt i)) <| List.range 1 20
- else text ""
- ]
- , editListField 0 "" [ removeButton (MedDel n) ]
- ]
- ) model.media
- , [ label [for "addmed"] [ text "Add medium" ]
- -- TODO: Move selection back to "" when a new medium has been added
- , inputSelect [id "addmed", onInput MedAdd] ""
- <| ("", "-- Add medium --")
- :: List.map (\(n,m) -> (n,m.single)) Lib.Gen.media
- ]
- ]
- ]
diff --git a/elm3/RelEdit/Main.elm b/elm3/RelEdit/Main.elm
deleted file mode 100644
index 16e317f0..00000000
--- a/elm3/RelEdit/Main.elm
+++ /dev/null
@@ -1,137 +0,0 @@
-module RelEdit.Main exposing (..)
-
-import Html exposing (..)
-import Browser
-import Browser.Navigation exposing (load)
-import Lib.Html exposing (..)
-import Lib.Gen as Gen
-import Lib.Api as Api
-import Lib.Editsum as Editsum
-import RelEdit.General as General
-import RelEdit.Producers as Producers
-import RelEdit.Vn as Vn
-
-
-main : Program Gen.RelEdit Model Msg
-main = Browser.element
- { init = \e -> (init e, Cmd.none)
- , view = view
- , update = update
- , subscriptions = always Sub.none
- }
-
-
-type alias Model =
- { state : Api.State
- , editsum : Editsum.Model
- , general : General.Model
- , producers : Producers.Model
- , vn : Vn.Model
- , id : Maybe Int
- }
-
-
-init : Gen.RelEdit -> Model
-init d =
- { state = Api.Normal
- , editsum = { authmod = d.authmod, editsum = d.editsum, locked = d.locked, hidden = d.hidden }
- , general = General.init d
- , producers = Producers.init d.producers
- , vn = Vn.init d.vn
- , id = d.id
- }
-
-
-new : Int -> String -> String -> Model
-new vid title orig =
- { state = Api.Normal
- , editsum = Editsum.new
- , general = General.new title orig
- , producers = Producers.init []
- , vn = Vn.init [{vid = vid, title = title}]
- , id = Nothing
- }
-
-
-encode : Model -> Gen.RelEditSend
-encode model =
- { editsum = model.editsum.editsum
- , hidden = model.editsum.hidden
- , locked = model.editsum.locked
- , catalog = model.general.catalog
- , doujin = model.general.doujin
- , freeware = model.general.freeware
- , gtin = Maybe.withDefault 0 model.general.gtinVal
- , lang = model.general.lang
- , minage = model.general.minage
- , notes = model.general.notes
- , original = model.general.original
- , patch = model.general.patch
- , rtype = model.general.rtype
- , released = model.general.released
- , title = model.general.title
- , uncensored = model.general.uncensored
- , website = model.general.website
- , resolution = model.general.resolution
- , voiced = model.general.voiced
- , ani_story = model.general.aniStory
- , ani_ero = model.general.aniEro
- , platforms = model.general.platforms
- , media = model.general.media
- , producers = List.map (\e -> { pid = e.pid, developer = e.developer, publisher = e.publisher }) model.producers.producers
- , vn = List.map (\e -> { vid = e.vid }) model.vn.vn
- }
-
-
-type Msg
- = Editsum Editsum.Msg
- | General General.Msg
- | Producers Producers.Msg
- | Vn Vn.Msg
- | Submit
- | Submitted Api.Response
-
-
-update : Msg -> Model -> (Model, Cmd Msg)
-update msg model =
- case msg of
- Editsum m -> ({ model | editsum = Editsum.update m model.editsum }, Cmd.none)
- General m -> ({ model | general = General.update m model.general }, Cmd.none)
- Producers m -> let (nm, c) = Producers.update m model.producers in ({ model | producers = nm }, Cmd.map Producers c)
- Vn m -> let (nm, c) = Vn.update m model.vn in ({ model | vn = nm }, Cmd.map Vn c)
-
- Submit ->
- let
- path =
- case model.id of
- Just id -> "/r" ++ String.fromInt id ++ "/edit"
- Nothing -> "/r/add"
- body = Gen.releditSendEncode (encode model)
- in ({ model | state = Api.Loading }, Api.post path body Submitted)
-
- Submitted (Gen.Changed id rev) -> (model, load <| "/r" ++ String.fromInt id ++ "." ++ String.fromInt rev)
- Submitted r -> ({ model | state = Api.Error r }, Cmd.none)
-
-
-isValid : Model -> Bool
-isValid model = not
- ( List.isEmpty model.general.lang
- || model.general.gtinVal == Nothing
- || model.producers.duplicates
- || model.vn.duplicates
- || List.isEmpty model.vn.vn
- )
-
-
-view : Model -> Html Msg
-view model =
- form_ Submit (model.state == Api.Loading)
- [ Html.map General <| General.general model.general
- , Html.map General <| General.format model.general
- , card "relations" "Relations" []
- [ Html.map Producers <| Producers.view model.producers
- , Html.map Vn <| Vn.view model.vn
- ]
- , Html.map Editsum <| Editsum.view model.editsum
- , submitButton "Submit" model.state (isValid model) False
- ]
diff --git a/elm3/RelEdit/New.elm b/elm3/RelEdit/New.elm
deleted file mode 100644
index 699e3c29..00000000
--- a/elm3/RelEdit/New.elm
+++ /dev/null
@@ -1,19 +0,0 @@
-module RelEdit.New exposing (main)
-
-import Browser
-import RelEdit.Main as Main
-
-
-type alias Flags =
- { id : Int
- , title : String
- , original : String
- }
-
-main : Program Flags Main.Model Main.Msg
-main = Browser.element
- { init = \f -> (Main.new f.id f.title f.original, Cmd.none)
- , view = Main.view
- , update = Main.update
- , subscriptions = always Sub.none
- }
diff --git a/elm3/RelEdit/Producers.elm b/elm3/RelEdit/Producers.elm
deleted file mode 100644
index 1af1f2ba..00000000
--- a/elm3/RelEdit/Producers.elm
+++ /dev/null
@@ -1,92 +0,0 @@
-module RelEdit.Producers exposing (Model, Msg, init, update, view)
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Html.Events exposing (..)
-import Lib.Html exposing (..)
-import Lib.Autocomplete as A
-import Lib.Gen as Gen
-import Lib.Util exposing (..)
-
-
-type alias Model =
- { producers : List Gen.RelEditProducers
- , search : A.Model Gen.ApiProducerResult
- , duplicates : Bool
- }
-
-
-init : List Gen.RelEditProducers -> Model
-init l =
- { producers = l
- , search = A.init
- , duplicates = False
- }
-
-
-type Msg
- = Del Int
- | SetRole Int String
- | Search (A.Msg Gen.ApiProducerResult)
-
-
-searchConfig : A.Config Msg Gen.ApiProducerResult
-searchConfig = { wrap = Search, id = "add-producer", source = A.producerSource }
-
-
-validate : Model -> Model
-validate model = { model | duplicates = hasDuplicates <| List.map .pid model.producers }
-
-
-update : Msg -> Model -> (Model, Cmd Msg)
-update msg model =
- case msg of
- Del i -> (validate { model | producers = delidx i model.producers }, Cmd.none)
- SetRole i s -> (validate { model | producers = modidx i (\e -> { e | developer = s == "d" || s == "b", publisher = s == "p" || s == "b" }) model.producers }
- , Cmd.none )
-
- Search m ->
- let (nm, c, res) = A.update searchConfig m model.search
- in case res of
- Nothing -> ({ model | search = nm }, c)
- Just r ->
- let nrow = { pid = r.id, name = r.name, developer = False, publisher = True }
- in (validate { model | search = A.clear nm, producers = model.producers ++ [nrow] }, c)
-
-
-
-view : Model -> Html Msg
-view model =
- let
- role e =
- case (e.developer, e.publisher) of
- (True, False) -> "d"
- (False, True) -> "p"
- _ -> "b"
-
- roles =
- [ ("d", "Developer")
- , ("p", "Publisher")
- , ("b", "Both")
- ]
-
- entry n e = editListRow ""
- [ editListField 1 "col-form-label single-line"
- [ a [href <| "/p" ++ String.fromInt e.pid, title e.name, target "_blank" ] [text e.name ] ]
- , editListField 1 ""
- [ inputSelect [onInput (SetRole n)] (role e) roles ]
- , editListField 0 "" [ removeButton (Del n) ]
- ]
-
- in cardRow "Producers" Nothing
- <| editList (List.indexedMap entry model.producers)
- ++ formGroups (
- (if model.duplicates
- then [ [ div [ class "invalid-feedback" ]
- [ text "The producers list contains duplicates." ] ] ]
- else []
- ) ++
- [ label [for "add-producer"] [text "Add producer"]
- :: A.view searchConfig model.search [placeholder "Producer", style "max-width" "400px"]
- ]
- )
diff --git a/elm3/RelEdit/Vn.elm b/elm3/RelEdit/Vn.elm
deleted file mode 100644
index 9f32aff4..00000000
--- a/elm3/RelEdit/Vn.elm
+++ /dev/null
@@ -1,77 +0,0 @@
-module RelEdit.Vn exposing (Model, Msg, init, update, view)
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Lib.Html exposing (..)
-import Lib.Gen as Gen
-import Lib.Util exposing (..)
-import Lib.Autocomplete as A
-
-
-type alias Model =
- { vn : List Gen.RelEditVn
- , search : A.Model Gen.ApiVNResult
- , duplicates : Bool
- }
-
-
-init : List Gen.RelEditVn -> Model
-init l =
- { vn = l
- , search = A.init
- , duplicates = False
- }
-
-
-type Msg
- = Del Int
- | Search (A.Msg Gen.ApiVNResult)
-
-
-searchConfig : A.Config Msg Gen.ApiVNResult
-searchConfig = { wrap = Search, id = "add-vn", source = A.vnSource }
-
-
-validate : Model -> Model
-validate model = { model | duplicates = hasDuplicates <| List.map .vid model.vn }
-
-
-update : Msg -> Model -> (Model, Cmd Msg)
-update msg model =
- case msg of
- Del i -> (validate { model | vn = delidx i model.vn }, Cmd.none)
- Search m ->
- let (nm, c, res) = A.update searchConfig m model.search
- in case res of
- Nothing -> ({ model | search = nm }, c)
- Just r ->
- let nrow = { vid = r.id, title = r.title }
- in (validate { model | search = A.clear nm, vn = model.vn ++ [nrow] }, c)
-
-
-view : Model -> Html Msg
-view model =
- let
- entry n e = editListRow "row--ai-center"
- [ editListField 1 "col-form-label single-line"
- [ a [href <| "/v" ++ String.fromInt e.vid, title e.title, target "_blank" ] [text e.title ] ]
- , editListField 0 "" [ removeButton (Del n) ]
- ]
-
- in cardRow "Visual Novels" Nothing
- <| editList (List.indexedMap entry model.vn)
- ++ formGroups (
- (if model.duplicates
- then [ [ div [ class "invalid-feedback" ]
- [ text "The list contains duplicates. Make sure that the same visual novel is not listed multiple times." ] ] ]
- else []
- ) ++
- (if List.isEmpty model.vn
- then [ [ div [ class "invalid-feedback" ]
- [ text "Please make sure that at least one visual novel is selected." ] ] ]
- else []
- ) ++
- [ label [for "add-vn"] [text "Add visual novel"]
- :: A.view searchConfig model.search [placeholder "Visual Novel...", style "max-width" "400px"]
- ]
- )
diff --git a/elm3/StaffEdit/Main.elm b/elm3/StaffEdit/Main.elm
deleted file mode 100644
index 96d90343..00000000
--- a/elm3/StaffEdit/Main.elm
+++ /dev/null
@@ -1,220 +0,0 @@
-module StaffEdit.Main exposing (Model, Msg, main, new, view, update)
-
-import Html exposing (..)
-import Html.Events exposing (..)
-import Html.Attributes exposing (..)
-import Json.Encode as JE
-import Browser
-import Browser.Navigation exposing (load)
-import Lib.Util exposing (..)
-import Lib.Html exposing (..)
-import Lib.Gen as Gen
-import Lib.Api as Api
-import Lib.Editsum as Editsum
-
-
-main : Program Gen.StaffEdit Model Msg
-main = Browser.element
- { init = \e -> (init e, Cmd.none)
- , view = view
- , update = update
- , subscriptions = always Sub.none
- }
-
-
-type alias Model =
- { state : Api.State
- , editsum : Editsum.Model
- , alias : List Gen.StaffEditAlias
- , aliasDup : Bool
- , aid : Int
- , desc : String
- , gender : String
- , l_site : String
- , l_wp : String
- , l_twitter : String
- , l_anidb : Maybe Int
- , lang : String
- , id : Maybe Int
- }
-
-
-init : Gen.StaffEdit -> Model
-init d =
- { state = Api.Normal
- , editsum = { authmod = d.authmod, editsum = d.editsum, locked = d.locked, hidden = d.hidden }
- , alias = d.alias
- , aliasDup = False
- , aid = d.aid
- , desc = d.desc
- , gender = d.gender
- , l_site = d.l_site
- , l_wp = d.l_wp
- , l_twitter = d.l_twitter
- , l_anidb = d.l_anidb
- , lang = "ja"
- , id = d.id
- }
-
-
-new : Model
-new =
- { state = Api.Normal
- , editsum = Editsum.new
- , alias = [ { aid = -1, name = "", original = "", inuse = False } ]
- , aliasDup = False
- , aid = -1
- , desc = ""
- , gender = "unknown"
- , l_site = ""
- , l_wp = ""
- , l_twitter = ""
- , l_anidb = Nothing
- , lang = "ja"
- , id = Nothing
- }
-
-
-encode : Model -> Gen.StaffEditSend
-encode model =
- { editsum = model.editsum.editsum
- , hidden = model.editsum.hidden
- , locked = model.editsum.locked
- , aid = model.aid
- , alias = List.map (\e -> { aid = e.aid, name = e.name, original = e.original }) model.alias
- , desc = model.desc
- , gender = model.gender
- , l_anidb = model.l_anidb
- , l_site = model.l_site
- , l_twitter = model.l_twitter
- , l_wp = model.l_wp
- , lang = model.lang
- }
-
-
-newAid : Model -> Int
-newAid model =
- let id = Maybe.withDefault 0 <| List.minimum <| List.map .aid model.alias
- in if id >= 0 then -1 else id - 1
-
-
-type Msg
- = Editsum Editsum.Msg
- | Submit
- | Submitted Api.Response
- | Lang String
- | Website String
- | LWP String
- | LTwitter String
- | LAnidb String
- | Desc String
- | AliasDel Int
- | AliasName Int String
- | AliasOrig Int String
- | AliasMain Int Bool
- | AliasAdd
-
-
-validate : Model -> Model
-validate model = { model | aliasDup = hasDuplicates <| List.map (\e -> (e.name, e.original)) model.alias }
-
-
-update : Msg -> Model -> (Model, Cmd Msg)
-update msg model =
- case msg of
- Editsum m -> ({ model | editsum = Editsum.update m model.editsum }, Cmd.none)
- Lang s -> ({ model | lang = s }, Cmd.none)
- Website s -> ({ model | l_site = s }, Cmd.none)
- LWP s -> ({ model | l_wp = s }, Cmd.none)
- LTwitter s -> ({ model | l_twitter = s }, Cmd.none)
- LAnidb s -> ({ model | l_anidb = if s == "" then Nothing else String.toInt s }, Cmd.none)
- Desc s -> ({ model | desc = s }, Cmd.none)
-
- AliasDel i -> (validate { model | alias = delidx i model.alias }, Cmd.none)
- AliasName i s -> (validate { model | alias = modidx i (\e -> { e | name = s }) model.alias }, Cmd.none)
- AliasOrig i s -> (validate { model | alias = modidx i (\e -> { e | original = s }) model.alias }, Cmd.none)
- AliasMain n _ -> ({ model | aid = n }, Cmd.none)
- AliasAdd -> ({ model | alias = model.alias ++ [{ aid = newAid model, name = "", original = "", inuse = False }] }, Cmd.none)
-
- Submit ->
- let
- path =
- case model.id of
- Just id -> "/s" ++ String.fromInt id ++ "/edit"
- Nothing -> "/s/add"
- body = Gen.staffeditSendEncode (encode model)
- in ({ model | state = Api.Loading }, Api.post path body Submitted)
-
- Submitted (Gen.Changed id rev) -> (model, load <| "/s" ++ String.fromInt id ++ "." ++ String.fromInt rev)
- Submitted r -> ({ model | state = Api.Error r }, Cmd.none)
-
-
-isValid : Model -> Bool
-isValid model = not
- ( model.aliasDup
- || List.any (\e -> e.name == "") model.alias
- )
-
-
-view : Model -> Html Msg
-view model =
- let
- nameEntry n e = editListRow ""
- [ editListField 0 ""
- [ inputRadio "main" (e.aid == model.aid) (AliasMain e.aid) ]
- , editListField 1 ""
- [ inputText "" e.name (AliasName n) <| (if e.name == "" then [class "is-invalid"] else []) ++ [placeholder "Name (romaji)"] ]
- , editListField 1 ""
- [ inputText "" e.original (AliasOrig n) [placeholder "Original name"] ]
- , editListField 0 ""
- [ if model.aid == e.aid || e.inuse then text "" else removeButton (AliasDel n) ]
- ]
-
- names = cardRow "Name(s)" (Just "Selected name = primary name.")
- <| editList (List.indexedMap nameEntry model.alias)
- ++ formGroups (
- (if model.aliasDup
- then [ [ div [ class "invalid-feedback" ]
- [ text "The list contains duplicate aliases." ] ] ]
- else []
- ) ++
- [ [ button [type_ "button", class "btn", tabindex 10, onClick AliasAdd] [ text "Add alias" ] ]
- , [ div [ class "form-group__help" ]
- [ text "Aliases can only be removed if they are not selected as this entry's primary name and if they are not credited in visual novel entries."
- , text " In some cases it happens that an alias can not be removed even when there are no visible credits for it."
- , text " This means that the alias is still credited from a deleted entry. A moderator can fix this for you."
- ]
- ]
- ]
- )
-
- meta = cardRow "Meta" Nothing <| formGroups
- [ [ label [for "lang"] [ text "Primary language" ]
- , inputSelect [id "lang", name "lang", onInput Lang] model.lang Gen.languages
- ]
- , [ label [for "website"] [ text "Official Website" ]
- , inputText "website" model.l_site Website [pattern Gen.weburlPattern]
- ]
- , [ label [] [ text "Wikipedia" ]
- , p [] [ text "https://en.wikipedia.org/wiki/", inputText "l_wp" model.l_wp LWP [class "form-control--inline", maxlength 100] ]
- ]
- , [ label [] [ text "Twitter username" ]
- , p [] [ text "https://twitter.com/", inputText "l_twitter" model.l_twitter LTwitter [class "form-control--inline", maxlength 100] ]
- ]
- , [ label [] [ text "AniDB creator ID" ]
- , p []
- [ text "https://anidb.net/cr"
- , inputText "l_anidb" (Maybe.withDefault "" (Maybe.map String.fromInt model.l_anidb))
- LAnidb [class "form-control--inline", maxlength 10, pattern "^[0-9]*$"]
- ]
- ]
- ]
-
- desc = cardRow "Description" (Just "English please!") <| formGroup
- [ inputTextArea "desc" model.desc Desc [rows 8] ]
-
- in form_ Submit (model.state == Api.Loading)
- [ card "general" "General info" [] [ names, meta, desc ]
- , Html.map Editsum <| Editsum.view model.editsum
- , submitButton "Submit" model.state (isValid model) False
- ]
diff --git a/elm3/StaffEdit/New.elm b/elm3/StaffEdit/New.elm
deleted file mode 100644
index 64e58517..00000000
--- a/elm3/StaffEdit/New.elm
+++ /dev/null
@@ -1,12 +0,0 @@
-module StaffEdit.New exposing (main)
-
-import Browser
-import StaffEdit.Main as Main
-
-main : Program () Main.Model Main.Msg
-main = Browser.element
- { init = always (Main.new, Cmd.none)
- , view = Main.view
- , update = Main.update
- , subscriptions = always Sub.none
- }
diff --git a/elm3/UVNList/Options.elm b/elm3/UVNList/Options.elm
deleted file mode 100644
index 2b7f0c1a..00000000
--- a/elm3/UVNList/Options.elm
+++ /dev/null
@@ -1,37 +0,0 @@
-module UVNList.Options exposing (main)
-
--- TODO: Actually implement the Edit form & remove functionality
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Html.Events exposing (..)
-import Browser
-import Lib.Html exposing (..)
-
-
-main : Program () Model Msg
-main = Browser.element
- { init = always ((), Cmd.none)
- , subscriptions = always Sub.none
- , view = view
- , update = \_ _ -> ((), Cmd.none)
- }
-
-type alias Msg = ()
-type alias Model = ()
-
--- XXX: This dropdown thing relies on the fact that the JS code to find and
--- update dropdowns is run *after* all Elm objects have initialized, but this
--- is pretty fragile and may break if we ever update our view. This should be
--- made more reliable - either by making sure the dropdown-JS can handle DOM
--- changes or by moving the handling into Elm.
-view : Model -> Html Msg
-view model =
- div [class "dropdown"]
- [ a [href "#", class "more-button more-button--light dropdown__toggle d-block"]
- [ span [ class "more-button__dots" ] [] ]
- , div [class "dropdown-menu"]
- [ a [href "#", class "dropdown-menu__item"] [ text "Edit" ]
- , a [href "#", class "dropdown-menu__item"] [ text "Remove" ]
- ]
- ]
diff --git a/elm3/UVNList/Status.elm b/elm3/UVNList/Status.elm
deleted file mode 100644
index 7a2782d2..00000000
--- a/elm3/UVNList/Status.elm
+++ /dev/null
@@ -1,77 +0,0 @@
-module UVNList.Status exposing (main)
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Html.Events exposing (..)
-import Json.Encode as JE
-import Browser
-import Lib.Api as Api
-import Lib.Html exposing (..)
-import Lib.Util exposing (lookup)
-import Lib.Gen as Gen
-
-
-main : Program Flags Model Msg
-main = Browser.element
- { init = \f -> (init f, Cmd.none)
- , subscriptions = always Sub.none
- , view = view
- , update = update
- }
-
-type alias Flags =
- { uid : Int
- , vid : Int
- , status : Int
- }
-
-type alias Model =
- { state : Api.State
- , flags : Flags
- }
-
-init : Flags -> Model
-init f =
- { state = Api.Normal
- , flags = f
- }
-
-
-encodeForm : Model -> JE.Value
-encodeForm o = JE.object
- [ ("uid", JE.int o.flags.uid)
- , ("vid", JE.int o.flags.vid)
- , ("status", JE.int o.flags.status) ]
-
-
-type Msg
- = Input String
- | Saved Api.Response
-
-
-update : Msg -> Model -> (Model, Cmd Msg)
-update msg model =
- case msg of
- Input s ->
- let flags = model.flags
- nflags = { flags | status = Maybe.withDefault 0 <| String.toInt s }
- nmodel = { model | flags = nflags, state = Api.Loading }
- in ( nmodel
- , Api.post "/u/setvnstatus" (encodeForm nmodel) Saved )
-
- Saved Gen.Success -> ({ model | state = Api.Normal }, Cmd.none)
- Saved e -> ({ model | state = Api.Error e }, Cmd.none)
-
-
-view : Model -> Html Msg
-view model =
- -- TODO: Display error somewhere
- if model.state == Api.Loading
- then div [ class "spinner spinner--md" ] []
- else div []
- [ text <| Maybe.withDefault "" <| lookup model.flags.status Gen.vnlistStatus
- , inputSelect
- [ class "form-control--table-edit form-control--table-edit-overlay", onInput Input ]
- (String.fromInt model.flags.status)
- (List.map (\(a,b) -> (String.fromInt a, b)) Gen.vnlistStatus)
- ]
diff --git a/elm3/UVNList/Vote.elm b/elm3/UVNList/Vote.elm
deleted file mode 100644
index 2834e25e..00000000
--- a/elm3/UVNList/Vote.elm
+++ /dev/null
@@ -1,103 +0,0 @@
-module UVNList.Vote exposing (main)
-
--- XXX: There's some unobvious and unintuitive behavior when removing a vote:
--- If the VN isn't also in the user's 'vnlist', then the VN entry will be
--- removed from the user's list and this is only visible on a page refresh. A
--- clean solution to this is to merge the 'votes' and 'vnlist' tables so that
--- there's always a 'vnlist' entry that remains. This is best done after VNDBv2
--- has been decommissioned.
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Html.Events exposing (..)
-import Json.Encode as JE
-import Browser
-import Regex
-import Lib.Api as Api
-import Lib.Gen as Gen
-
-
-main : Program Flags Model Msg
-main = Browser.element
- { init = \f -> (init f, Cmd.none)
- , subscriptions = always Sub.none
- , view = view
- , update = update
- }
-
-type alias Flags =
- { uid : Int
- , vid : Int
- , vote : String
- }
-
-type alias Model =
- { state : Api.State
- , flags : Flags
- , text : String
- , valid : Bool
- }
-
-init : Flags -> Model
-init f =
- { state = Api.Normal
- , flags = f
- , text = f.vote
- , valid = True
- }
-
-
-encodeForm : Model -> JE.Value
-encodeForm o = JE.object
- [ ("uid", JE.int o.flags.uid)
- , ("vid", JE.int o.flags.vid)
- , ("vote", JE.string o.text) ]
-
-
-type Msg
- = Input String
- | Save
- | Saved Api.Response
-
-
-update : Msg -> Model -> (Model, Cmd Msg)
-update msg model =
- case msg of
- Input s ->
- ( { model | text = s
- , valid = Regex.contains (Maybe.withDefault Regex.never <| Regex.fromString Gen.vnvotePattern) s
- }
- , Cmd.none
- )
-
- Save ->
- if model.valid && model.text /= model.flags.vote
- then ( { model | state = Api.Loading }
- , Api.post "/u/setvote" (encodeForm model) Saved )
- else (model, Cmd.none)
-
- Saved Gen.Success ->
- let flags = model.flags
- nflags = { flags | vote = model.text }
- in ({ model | flags = nflags, state = Api.Normal }, Cmd.none)
-
- Saved e -> ({ model | state = Api.Error e }, Cmd.none)
-
-
-view : Model -> Html Msg
-view model =
- -- TODO: Display error somewhere
- -- TODO: Save when pressing enter
- if model.state == Api.Loading
- then
- div [ class "spinner spinner--md" ] []
- else
- input
- [ type_ "text"
- , pattern Gen.vnvotePattern
- , class "form-control form-control--table-edit form-control--stealth"
- , classList [("is-invalid", not model.valid)]
- , value model.text
- , onInput Input
- , onBlur Save
- ] []
diff --git a/elm3/User/Login.elm b/elm3/User/Login.elm
deleted file mode 100644
index 09aa0aa7..00000000
--- a/elm3/User/Login.elm
+++ /dev/null
@@ -1,86 +0,0 @@
-module User.Login exposing (main)
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Html.Events exposing (..)
-import Json.Encode as JE
-import Browser
-import Browser.Navigation exposing (load)
-import Lib.Api as Api
-import Lib.Gen as Gen
-import Lib.Html exposing (..)
-
-
-main : Program () Model Msg
-main = Browser.element
- { init = always (Model "" "" Api.Normal, Cmd.none)
- , subscriptions = always Sub.none
- , view = view
- , update = update
- }
-
-
-encodeForm : Model -> JE.Value
-encodeForm o = JE.object
- [ ("username", JE.string o.username)
- , ("password", JE.string o.password) ]
-
-
-type alias Model =
- { username : String
- , password : String
- , state : Api.State
- }
-
-
-type Msg
- = Username String
- | Password String
- | Submit
- | Submitted Api.Response
-
-
-update : Msg -> Model -> (Model, Cmd Msg)
-update msg model =
- case msg of
- Username n -> ({ model | username = n }, Cmd.none)
- Password n -> ({ model | password = n }, Cmd.none)
-
- Submit -> ( { model | state = Api.Loading }
- , Api.post "/u/login" (encodeForm model) Submitted
- )
-
- Submitted Gen.Success -> (model, load "/")
- Submitted e -> ({ model | state = Api.Error e }, Cmd.none)
-
-
-view : Model -> Html Msg
-view model = form_ Submit (model.state == Api.Loading)
- [ div [ class "card card--white card--no-separators flex-expand small-card mb-5" ]
- [ div [ class "card__header" ] [ div [ class "card__title" ] [ text "Log in" ]]
- , case model.state of
- Api.Error e ->
- div [ class "card__section card__section--error fs-medium" ]
- [ h5 [] [ text "Error" ]
- , ul []
- [ li [] [ text <| Api.showResponse e ]
- , li [] [ text "If you have not used this login form since October 2014, your account has likely been disabled. You can reset your password to regain access." ]
- ]
- ]
- _ -> text ""
- , div [ class "card__section fs-medium" ]
- [ div [ class "form-group" ] [ inputText "username" model.username Username [placeholder "Username", required True, pattern "[a-z0-9-]{2,15}"] ]
- , div [ class "form-group" ] [ inputText "password" model.password Password [placeholder "Password", required True, minlength 4, maxlength 500, type_ "password"] ]
- , div [ class "d-flex jc-between" ] [ a [ href "/u/newpass" ] [ text "Forgot your password?" ] ]
- ]
- , div [ class "card__section" ]
- [ div [ class "d-flex jc-between" ]
- [ a [ class "btn btn--subtle", href "/u/register" ] [ text "Create an account" ]
- , if model.state == Api.Loading
- then div [ class "spinner spinner--md pull-right" ] []
- else text ""
- , input [ type_ "submit", class "btn", tabindex 10, value "Log in" ] []
- ]
- ]
- ]
- ]
diff --git a/elm3/User/PassReset.elm b/elm3/User/PassReset.elm
deleted file mode 100644
index 9d6130fa..00000000
--- a/elm3/User/PassReset.elm
+++ /dev/null
@@ -1,90 +0,0 @@
-module User.PassReset exposing (main)
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Html.Events exposing (..)
-import Browser
-import Json.Encode as JE
-import Lib.Api as Api
-import Lib.Gen as Gen
-import Lib.Html exposing (..)
-
-
-main : Program () Model Msg
-main = Browser.element
- { init = always (Model "" False Api.Normal, Cmd.none)
- , subscriptions = always Sub.none
- , view = view
- , update = update
- }
-
-
-encodeForm : Model -> JE.Value
-encodeForm o = JE.object
- [ ("email", JE.string o.email) ]
-
-
-type alias Model =
- { email : String
- , success : Bool
- , state : Api.State
- }
-
-
-type Msg
- = EMail String
- | Submit
- | Submitted Api.Response
-
-
-update : Msg -> Model -> (Model, Cmd Msg)
-update msg model =
- case msg of
- EMail n -> ({ model | email = n }, Cmd.none)
-
- Submit -> ( { model | state = Api.Loading }
- , Api.post "/u/newpass" (encodeForm model) Submitted
- )
-
- Submitted Gen.Success -> ({ model | success = True }, Cmd.none)
- Submitted e -> ({ model | state = Api.Error e }, Cmd.none)
-
-
-view : Model -> Html Msg
-view model = form_ Submit (model.state == Api.Loading)
- [ div [ class "card card--white card--no-separators flex-expand small-card mb-5" ] <|
- [ div [ class "card__header" ] [ div [ class "card__title" ] [ text "Reset password" ]]
- , case model.state of
- Api.Error e ->
- div [ class "card__section card__section--error fs-medium" ]
- [ h5 [] [ text "Error" ]
- , text <| Api.showResponse e
- ]
- _ -> text ""
- ] ++ if model.success
- then
- [ div [ class "card__section fs-medium" ]
- [ text "Your password has been reset and instructions to set a new one should reach your mailbox in a few minutes." ]
- ]
- else
- [
- div [ class "card__section fs-medium" ]
- [ div [ class "form-group" ]
- [ div [ class "form-group__help" ]
- [ text "Forgot your password and can\'t login to VNDB anymore?"
- , br [] []
- , text "Don't worry! Just fill in the email address you used to register on VNDB, and you'll receive instructions to set a new password within a few minutes!"
- ]
- , inputText "email" model.email EMail [required True, type_ "email"]
- ]
- ]
- , div [ class "card__section" ]
- [ div [ class "d-flex jc-end" ]
- [ if model.state == Api.Loading
- then div [ class "spinner spinner--md pull-right" ] []
- else text ""
- , input [ type_ "submit", class "btn", tabindex 10, value "Submit" ] []
- ]
- ]
- ]
- ]
diff --git a/elm3/User/PassSet.elm b/elm3/User/PassSet.elm
deleted file mode 100644
index b85f715a..00000000
--- a/elm3/User/PassSet.elm
+++ /dev/null
@@ -1,94 +0,0 @@
-module User.PassSet exposing (main)
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Html.Events exposing (..)
-import Json.Encode as JE
-import Browser
-import Browser.Navigation exposing (load)
-import Lib.Api as Api
-import Lib.Gen as Gen
-import Lib.Html exposing (..)
-
-
-main : Program String Model Msg
-main = Browser.element
- { init = \s ->
- ( { url = s
- , pass1 = ""
- , pass2 = ""
- , badPass = False
- , state = Api.Normal
- }
- , Cmd.none)
- , subscriptions = always Sub.none
- , view = view
- , update = update
- }
-
-
-encodeForm : Model -> JE.Value
-encodeForm o = JE.object
- [ ("pass", JE.string o.pass1) ]
-
-
-type alias Model =
- { url : String
- , pass1 : String
- , pass2 : String
- , badPass : Bool
- , state : Api.State
- }
-
-
-type Msg
- = Pass1 String
- | Pass2 String
- | Submit
- | Submitted Api.Response
-
-
-update : Msg -> Model -> (Model, Cmd Msg)
-update msg model =
- case msg of
- Pass1 n -> ({ model | pass1 = n, badPass = False }, Cmd.none)
- Pass2 n -> ({ model | pass2 = n, badPass = False }, Cmd.none)
-
- Submit ->
- if model.pass1 /= model.pass2
- then ({ model | badPass = True }, Cmd.none)
- else ( { model | state = Api.Loading }
- , Api.post model.url (encodeForm model) Submitted)
-
- Submitted Gen.Success -> (model, load "/")
- Submitted e -> ({ model | state = Api.Error e }, Cmd.none)
-
-
-view : Model -> Html Msg
-view model =
- let err s =
- div [ class "card__section card__section--error fs-medium" ]
- [ h5 [] [ text "Error" ]
- , text s
- ]
- in form_ Submit (model.state == Api.Loading)
- [ div [ class "card card--white card--no-separators flex-expand small-card mb-5" ]
- [ div [ class "card__header" ] [ div [ class "card__title" ] [ text "Set password" ]]
- , case model.state of
- Api.Error e -> err <| Api.showResponse e
- _ -> text ""
- , if model.badPass then err "Passwords to not match" else text ""
- , div [ class "card__section fs-medium" ]
- [ div [ class "form-group" ] [ inputText "pass1" model.pass1 Pass1 [placeholder "New password", required True, minlength 4, maxlength 500, type_ "password"] ]
- , div [ class "form-group" ] [ inputText "pass2" model.pass2 Pass2 [placeholder "Repeat", required True, minlength 4, maxlength 500, type_ "password"] ]
- ]
- , div [ class "card__section" ]
- [ div [ class "d-flex jc-end" ]
- [ if model.state == Api.Loading
- then div [ class "spinner spinner--md pull-right" ] []
- else text ""
- , input [ type_ "submit", class "btn", tabindex 10, value "Submit" ] []
- ]
- ]
- ]
- ]
diff --git a/elm3/User/Register.elm b/elm3/User/Register.elm
deleted file mode 100644
index e214cef1..00000000
--- a/elm3/User/Register.elm
+++ /dev/null
@@ -1,108 +0,0 @@
-module User.Register exposing (main)
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Html.Events exposing (..)
-import Browser
-import Json.Encode as JE
-import Lib.Gen as Gen
-import Lib.Api as Api
-import Lib.Html exposing (..)
-
-
-main : Program () Model Msg
-main = Browser.element
- { init = always (Model "" "" 0 False Api.Normal, Cmd.none)
- , subscriptions = always Sub.none
- , view = view
- , update = update
- }
-
-
-encodeForm : Model -> JE.Value
-encodeForm o = JE.object
- [ ("username", JE.string o.username)
- , ("email", JE.string o.email )
- , ("vns", JE.int o.vns )]
-
-
-type alias Model =
- { username : String
- , email : String
- , vns : Int
- , success : Bool
- , state : Api.State
- }
-
-type Msg
- = Username String
- | EMail String
- | VNs String
- | Submit
- | Submitted Api.Response
-
-
-update : Msg -> Model -> (Model, Cmd Msg)
-update msg model =
- case msg of
- Username n -> ({ model | username = n }, Cmd.none)
- EMail n -> ({ model | email = n }, Cmd.none)
- VNs n -> ({ model | vns = Maybe.withDefault 0 (String.toInt n) }, Cmd.none)
-
- Submit -> ( { model | state = Api.Loading }
- , Api.post "/u/register" (encodeForm model) Submitted)
-
- Submitted Gen.Success -> ({ model | state = Api.Normal, success = True }, Cmd.none)
- Submitted e -> ({ model | state = Api.Error e}, Cmd.none)
-
-
-view : Model -> Html Msg
-view model = form_ Submit (model.state == Api.Loading)
- [ div [ class "card card--white card--no-separators flex-expand small-card mb-5" ] <|
- [ div [ class "card__header" ] [ div [ class "card__title" ] [ text "Register" ]]
- , case model.state of
- Api.Error e ->
- div [ class "card__section card__section--error fs-medium" ]
- [ h5 [] [ text "Error" ]
- , text <| Api.showResponse e
- ]
- _ -> text ""
- ] ++ if model.success
- then
- [ div [ class "card__section fs-medium" ]
- [ text "Your account has been created! In a few minutes, you should receive an email with instructions to set a password and activate your account." ]
- ]
- else
- [
- div [ class "card__section fs-medium" ]
- [ div [ class "form-group" ]
- [ label [ for "username" ] [ text "Username" ]
- , inputText "username" model.username Username [required True, pattern "[a-z0-9-]{2,15}"]
- , div [ class "form-group__help" ] [ text "Preferred username. Must be lowercase and can only consist of alphanumeric characters." ]
- ]
- , div [ class "form-group" ]
- [ label [ for "email" ] [ text "Email" ]
- , inputText "email" model.email EMail [required True, type_ "email", pattern Gen.emailPattern]
- , div [ class "form-group__help" ]
- [ text "Your email address will only be used in case you lose your password. We will never send spam or newsletters unless you explicitly ask us for it or we get hacked." ]
- ]
- , div [ class "form-group" ]
- [ label [ for "vns" ] [ text "How many visual novels are there in the database?" ]
- , inputText "vns" (String.fromInt model.vns) VNs [required True, pattern "[0-9]+"]
- , div [ class "form-group__help" ]
- [ text "Anti-bot question, you can find the answer on the "
- , a [ href "/", target "_blank" ] [ text "main page" ]
- , text "."
- ]
- ]
- ]
- , div [ class "card__section" ]
- [ div [ class "d-flex jc-end" ]
- [ if model.state == Api.Loading
- then div [ class "spinner spinner--md pull-right" ] []
- else text ""
- , input [ type_ "submit", class "btn", tabindex 10, value "Submit" ] []
- ]
- ]
- ]
- ]
diff --git a/elm3/User/Settings.elm b/elm3/User/Settings.elm
deleted file mode 100644
index 43ab941a..00000000
--- a/elm3/User/Settings.elm
+++ /dev/null
@@ -1,206 +0,0 @@
-module User.Settings exposing (main)
-
-import Bitwise exposing (..)
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Html.Events exposing (..)
-import Browser
-import Browser.Navigation exposing (reload)
-import Lib.Html exposing (..)
-import Lib.Gen as Gen
-import Lib.Api as Api
-
-
-main : Program Gen.UserEdit Model Msg
-main = Browser.element
- { init = init
- , view = view
- , update = update
- , subscriptions = always Sub.none
- }
-
-
-type alias Model =
- { state : Api.State
- , saved : Bool
- , data : Gen.UserEdit
- , cpass : Bool
- , pass1 : String
- , pass2 : String
- , opass : String
- , passNeq : Bool
- }
-
-
-init : Gen.UserEdit -> (Model, Cmd Msg)
-init d =
- ({state = Api.Normal
- , saved = False
- , data = d
- , cpass = False
- , pass1 = ""
- , pass2 = ""
- , opass = ""
- , passNeq = False
- }, Cmd.none)
-
-
-encode : Model -> Gen.UserEditSend
-encode model =
- { hide_list = model.data.hide_list
- , ign_votes = model.data.ign_votes
- , mail = model.data.mail
- , password = if model.cpass then Just { old = model.opass, new = model.pass1 } else Nothing
- , perm = model.data.perm
- , show_nsfw = model.data.show_nsfw
- , spoilers = model.data.spoilers
- , tags_all = model.data.tags_all
- , tags_cont = model.data.tags_cont
- , tags_ero = model.data.tags_ero
- , tags_tech = model.data.tags_tech
- , traits_sexual = model.data.traits_sexual
- , username = model.data.username
- }
-
-
-type UpdateMsg
- = Username String
- | Email String
- | Perm Int Bool
- | IgnVotes Bool
- | HideList Bool
- | ShowNsfw Bool
- | TraitsSexual Bool
- | Spoilers String
- | TagsAll Bool
- | TagsCont Bool
- | TagsEro Bool
- | TagsTech Bool
-
-type Msg
- = Submit
- | Submitted Api.Response
- | Set UpdateMsg
- | CPass Bool
- | OPass String
- | Pass1 String
- | Pass2 String
-
-
-updateField : UpdateMsg -> Gen.UserEdit -> Gen.UserEdit
-updateField msg model =
- case msg of
- Username s -> { model | username = s }
- Email s -> { model | mail = s }
- Perm n b -> { model | perm = if b then or model.perm n else and model.perm (complement n) }
- IgnVotes b -> { model | ign_votes = b }
- HideList b -> { model | hide_list = b }
- ShowNsfw b -> { model | show_nsfw = b }
- TraitsSexual b -> { model | traits_sexual = b }
- Spoilers s -> { model | spoilers = Maybe.withDefault model.spoilers (String.toInt s) }
- TagsAll b -> { model | tags_all = b }
- TagsCont b -> { model | tags_cont = b }
- TagsEro b -> { model | tags_ero = b }
- TagsTech b -> { model | tags_tech = b }
-
-
-update : Msg -> Model -> (Model, Cmd Msg)
-update msg model =
- case msg of
- Set m -> ({ model | saved = False, data = updateField m model.data }, Cmd.none)
- CPass b -> ({ model | saved = False, cpass = b }, Cmd.none)
- OPass s -> ({ model | saved = False, opass = s }, Cmd.none)
- Pass1 s -> ({ model | saved = False, pass1 = s, passNeq = s /= model.pass2 }, Cmd.none)
- Pass2 s -> ({ model | saved = False, pass2 = s, passNeq = s /= model.pass1 }, Cmd.none)
-
- Submit ->
- let
- path = "/u" ++ String.fromInt model.data.id ++ "/edit"
- body = Gen.usereditSendEncode (encode model)
- in ({ model | state = Api.Loading }, Api.post path body Submitted)
-
- Submitted (Gen.Success) ->
- ( { model | state = Api.Normal, saved = True, cpass = False, opass = "", pass1 = "", pass2 = "" }
- , if model.cpass then reload else Cmd.none
- )
- Submitted r -> ({ model | state = Api.Error r }, Cmd.none)
-
-
-view : Model -> Html Msg
-view model =
- form_ Submit (model.state == Api.Loading)
- [ card "account" "Account info" [] <|
-
- [ cardRow "General" Nothing <| formGroups
- [ [ label [ for "username" ] [ text "Username" ]
- , inputText "username" model.data.username (Set << Username) [required True, maxlength 200, pattern "[a-z0-9-]{2,15}", disabled (not model.data.authmod)]
- ]
- , [ label [ for "email" ] [ text "Email address" ]
- , inputText "email" model.data.mail (Set << Email) [type_ "email", required True, pattern Gen.emailPattern]
- ]
- ]
-
- , cardRow "Password" Nothing <| formGroups <|
- [ label [ class "checkbox" ]
- [ inputCheck "" model.cpass CPass
- , text " Change password" ]
- ]
- :: if not model.cpass then [] else
- [ [ label [ class "opass" ] [ text "Current password" ]
- , inputText "opass" model.opass OPass [type_ "password", required True, minlength 4, maxlength 500]
- ]
- , [ label [ class "pass1" ] [ text "New password" ]
- , inputText "pass1" model.pass1 Pass1 [type_ "password", required True, minlength 4, maxlength 500]
- ]
- , [ label [ class "pass2" ] [ text "Repeat" ]
- , inputText "pass2" model.pass2 Pass2 [type_ "password", required True, minlength 4, maxlength 500, classList [("is-invalid", model.passNeq)]]
- , if model.passNeq
- then div [class "invalid-feedback"]
- [ text "Passwords do not match." ]
- else text ""
- ]
- ]
-
- ] ++ if not model.data.authmod then [] else
- [ cardRow "Mod options" Nothing <| formGroups
- [ [ label [] [ text "Permissions" ]
- ] ++ List.map (\(n,s) ->
- label [ class "checkbox" ] [ inputCheck "" (and model.data.perm n > 0) (Set << Perm n), text (" " ++ s) ]
- ) Gen.userPerms
- , [ label [] [ text "Other" ]
- , label [ class "checkbox" ] [ inputCheck "" model.data.ign_votes (Set << IgnVotes), text "Ignore votes in VN statistics" ]
- ]
- ]
- ]
-
- , card "preferences" "Preferences" [] <|
-
- [ cardRow "Privacy" Nothing <| formGroup
- [ label [ class "checkbox" ] [ inputCheck "" model.data.hide_list (Set << HideList), text "Hide my visual novel list, vote list and wishlist and exclude these lists from the database dumps and API" ] ]
-
- , cardRow "NSFW" Nothing <| formGroups
- [ [ label [ class "checkbox" ] [ inputCheck "" model.data.show_nsfw (Set << ShowNsfw), text "Disable warnings for images that are not safe for work" ] ]
- , [ label [ class "checkbox" ] [ inputCheck "" model.data.traits_sexual (Set << TraitsSexual), text "Show sexual traits by default on character pages" ] ]
- ]
-
- , cardRow "Spoilers" Nothing <| formGroup
- [ label [ for "spoilers" ] [ text "Default spoiler level" ]
- , inputSelect [onInput (Set << Spoilers)] (String.fromInt model.data.spoilers)
- [ ("0", "Hide spoilers")
- , ("1", "Show only minor spoilers")
- , ("2", "Show all spoilers")
- ]
- ]
-
- , cardRow "Tags" Nothing <| formGroups
- [ [ label [ class "checkbox" ] [ inputCheck "" model.data.tags_all (Set << TagsAll), text "Show all tags by default on visual novel pages (don't summarize)" ] ]
- , [ label [] [ text "Default tag categories on visual novel pages:" ]
- , label [ class "chexkbox" ] [ inputCheck "" model.data.tags_cont (Set << TagsCont), text "Content" ]
- , label [ class "chexkbox" ] [ inputCheck "" model.data.tags_ero (Set << TagsEro ), text "Sexual content" ]
- , label [ class "chexkbox" ] [ inputCheck "" model.data.tags_tech (Set << TagsTech), text "Technical" ]
- ]
- ]
- ]
-
- , submitButton (if model.saved then "Saved!" else "Save") model.state (not model.passNeq) False
- ]
diff --git a/elm3/VNEdit/General.elm b/elm3/VNEdit/General.elm
deleted file mode 100644
index 4a0ebaa2..00000000
--- a/elm3/VNEdit/General.elm
+++ /dev/null
@@ -1,155 +0,0 @@
-module VNEdit.General exposing (..)
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Html.Events exposing (..)
-import File exposing (File)
-import Json.Decode as JD
-import Lib.Html exposing (..)
-import Lib.Gen as Gen
-import Lib.Util exposing (..)
-import Lib.Api as Api
-
-
-type alias Model =
- { desc : String
- , image : Int
- , imgState : Api.State
- , img_nsfw : Bool
- , length : Int
- , l_renai : String
- , l_wp : String
- , anime : String
- , animeList : List { aid : Int }
- , animeDuplicates : Bool
- }
-
-
-init : Gen.VNEdit -> Model
-init d =
- { desc = d.desc
- , image = d.image
- , imgState = Api.Normal
- , img_nsfw = d.img_nsfw
- , length = d.length
- , l_renai = d.l_renai
- , l_wp = d.l_wp
- , anime = String.join " " (List.map (.aid >> String.fromInt) d.anime)
- , animeList = d.anime
- , animeDuplicates = False
- }
-
-
-new : Model
-new =
- { desc = ""
- , image = 0
- , imgState = Api.Normal
- , img_nsfw = False
- , length = 0
- , l_renai = ""
- , l_wp = ""
- , anime = ""
- , animeList = []
- , animeDuplicates = False
- }
-
-
-type Msg
- = Desc String
- | Image String
- | ImgNSFW Bool
- | Length String
- | LWP String
- | LRenai String
- | Anime String
- | ImgUpload (List File)
- | ImgDone Api.Response
-
-
-update : Msg -> Model -> (Model, Cmd Msg)
-update msg model =
- case msg of
- Desc s -> ({ model | desc = s }, Cmd.none)
- Image s -> ({ model | image = if s == "" then 0 else Maybe.withDefault model.image (String.toInt s) }, Cmd.none)
- ImgNSFW b -> ({ model | img_nsfw = b }, Cmd.none)
- Length s -> ({ model | length = Maybe.withDefault 0 (String.toInt s) }, Cmd.none)
- LWP s -> ({ model | l_wp = s }, Cmd.none)
- LRenai s -> ({ model | l_renai = s }, Cmd.none)
-
- Anime s ->
- let lst = List.map (\e -> { aid = Maybe.withDefault 0 (String.toInt e) }) (String.words s)
- in ({ model | anime = s, animeList = lst, animeDuplicates = hasDuplicates <| List.map .aid lst }, Cmd.none)
-
- ImgUpload [i] -> ({ model | imgState = Api.Loading }, Api.postImage Api.Cv i ImgDone)
- ImgUpload _ -> (model, Cmd.none)
-
- ImgDone (Gen.Image id _ _) -> ({ model | image = id, imgState = Api.Normal }, Cmd.none)
- ImgDone r -> ({ model | image = 0, imgState = Api.Error r }, Cmd.none)
-
-
-view : Model -> (Msg -> a) -> List (Html a) -> Html a
-view model wrap titles = card "general" "General info" [] <|
- titles ++ List.map (Html.map wrap)
- [ cardRow "Description" (Just "English please!") <| formGroup
- [ inputTextArea "desc" model.desc Desc [rows 8]
- , div [class "form-group__help"]
- [ text "Short description of the main story. Please do not include untagged spoilers,"
- , text " and don't forget to list the source in case you didn't write the description yourself."
- , text " Formatting codes are allowed."
- ]
- ]
- , cardRow "Image" Nothing
- [ div [class "row"]
- [ div [class "col-md col-md--1"]
- [ div [style "max-width" "200px", style "margin-bottom" "8px"]
- [ dbImg "cv" (if model.imgState == Api.Loading then -1 else model.image) [] Nothing ]
- ]
- , div [class "col-md col-md--2"] <| formGroups
- [ [ label [for "img"] [ text "Upload new image" ]
- , input [type_ "file", class "text", name "img", id "img", Api.onFileChange ImgUpload, disabled (model.imgState == Api.Loading) ] []
- , case model.imgState of
- Api.Error r -> div [class "invalid-feedback"] [text <| Api.showResponse r]
- _ -> text ""
- , div [class "form-group__help"]
- [ text "Preferably the cover of the CD/DVD/package. Image must be in JPEG or PNG format and at most 5MB. Images larger than 256x400 will automatically be resized." ]
- ]
- , [ label [for "img_id"] [ text "Image ID" ]
- , inputText "img_id" (String.fromInt model.image) Image [pattern "^[0-9]+$", disabled (model.imgState == Api.Loading)]
- , div [class "form-group__help"]
- [ text "Use a VN image that is already on the server. Set to '0' to remove the current image." ]
- ]
- , [ label [for "img_nsfw"] [ text "NSFW" ]
- , label [class "checkbox"]
- [ inputCheck "img_nsfw" model.img_nsfw ImgNSFW
- , text " Not safe for work" ]
- , div [class "form-group__help"]
- [ text "Please check this option if the image contains nudity, gore, or is otherwise not safe in a work-friendly environment." ]
- ]
- ]
- ]
- ]
- , cardRow "Properties" Nothing <| formGroups
- [ [ label [for "length"] [ text "Length" ]
- , inputSelect [id "length", name "length", onInput Length]
- (String.fromInt model.length)
- (List.map (\(a,b) -> (String.fromInt a, b)) Gen.vnLengths)
- ]
- , [ label [] [ text "External links" ]
- , p [] [ text "http://en.wikipedia.org/wiki/", inputText "l_wp" model.l_wp LWP [class "form-control--inline", maxlength 100] ]
- , p [] [ text "http://renai.us/game/", inputText "l_renai" model.l_renai LRenai [class "form-control--inline", maxlength 100], text ".shtml" ]
- ]
- -- TODO: Nicer list-editing and search suggestions for anime
- , [ label [ for "anime" ] [ text "Anime" ]
- , inputText "anime" model.anime Anime [pattern "^[ 0-9]*$"]
- , if model.animeDuplicates
- then div [class "invalid-feedback"] [ text "There are duplicate anime." ]
- else text ""
- , div [class "form-group__help"]
- [ text "Whitespace separated list of AniDB anime IDs. E.g. \"1015 3348\" will add Shingetsutan Tsukihime and Fate/stay night as related anime."
- , br [] []
- , text "Note: It can take a few minutes for the anime titles to appear on the VN page."
- ]
- ]
- ]
- ]
diff --git a/elm3/VNEdit/Main.elm b/elm3/VNEdit/Main.elm
deleted file mode 100644
index f613e62e..00000000
--- a/elm3/VNEdit/Main.elm
+++ /dev/null
@@ -1,199 +0,0 @@
-module VNEdit.Main exposing (Model, Msg, main, new, view, update)
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Html.Lazy exposing (..)
-import Json.Encode as JE
-import Browser
-import Browser.Navigation exposing (load)
-import Lib.Html exposing (..)
-import Lib.Gen as Gen
-import Lib.Api as Api
-import Lib.Editsum as Editsum
-import VNEdit.Titles as Titles
-import VNEdit.General as General
-import VNEdit.Seiyuu as Seiyuu
-import VNEdit.Staff as Staff
-import VNEdit.Screenshots as Scr
-import VNEdit.Relations as Rel
-
-
-main : Program Gen.VNEdit Model Msg
-main = Browser.element
- { init = \e -> (init e, Cmd.none)
- , view = view
- , update = update
- , subscriptions = always Sub.none
- }
-
-
-type alias Model =
- { state : Api.State
- , new : Bool
- , editsum : Editsum.Model
- , l_encubed : String
- , titles : Titles.Model
- , general : General.Model
- , staff : Staff.Model
- , seiyuu : Seiyuu.Model
- , relations : Rel.Model
- , screenshots : Scr.Model
- , id : Maybe Int
- , dupVNs : List Gen.ApiVNResult
- }
-
-
-init : Gen.VNEdit -> Model
-init d =
- { state = Api.Normal
- , new = False
- , editsum = { authmod = d.authmod, editsum = d.editsum, locked = d.locked, hidden = d.hidden }
- , l_encubed = d.l_encubed
- , titles = Titles.init d
- , general = General.init d
- , staff = Staff.init d.staff
- , seiyuu = Seiyuu.init d.seiyuu d.chars
- , relations = Rel.init d.relations
- , screenshots = Scr.init d.screenshots d.releases
- , id = d.id
- , dupVNs = []
- }
-
-
-new : Model
-new =
- { state = Api.Normal
- , new = True
- , editsum = Editsum.new
- , l_encubed = ""
- , titles = Titles.new
- , general = General.new
- , staff = Staff.init []
- , seiyuu = Seiyuu.init [] []
- , relations = Rel.init []
- , screenshots = Scr.init [] []
- , id = Nothing
- , dupVNs = []
- }
-
-
-encode : Model -> Gen.VNEditSend
-encode model =
- { editsum = model.editsum.editsum
- , hidden = model.editsum.hidden
- , locked = model.editsum.locked
- , l_encubed = model.l_encubed
- , title = model.titles.title
- , original = model.titles.original
- , alias = model.titles.alias
- , desc = model.general.desc
- , image = model.general.image
- , img_nsfw = model.general.img_nsfw
- , length = model.general.length
- , l_renai = model.general.l_renai
- , l_wp = model.general.l_wp
- , anime = model.general.animeList
- , staff = List.map (\e -> { aid = e.aid, role = e.role, note = e.note }) model.staff.staff
- , seiyuu = List.map (\e -> { aid = e.aid, cid = e.cid, note = e.note }) model.seiyuu.seiyuu
- , screenshots = List.map (\e -> { scr = e.scr, rid = e.rid, nsfw = e.nsfw }) model.screenshots.screenshots
- , relations = List.map (\e -> { vid = e.vid, relation = e.relation, official = e.official }) model.relations.relations
- }
-
-
-type Msg
- = Editsum Editsum.Msg
- | Submit
- | Submitted Api.Response
- | Titles Titles.Msg
- | General General.Msg
- | Staff Staff.Msg
- | Seiyuu Seiyuu.Msg
- | Relations Rel.Msg
- | Screenshots Scr.Msg
- | CheckDup
- | RecvDup Api.Response
-
-
-update : Msg -> Model -> (Model, Cmd Msg)
-update msg model =
- case msg of
- Editsum m -> ({ model | editsum = Editsum.update m model.editsum }, Cmd.none)
- Titles m -> ({ model | titles = Titles.update m model.titles, dupVNs = [] }, Cmd.none)
-
- Submit ->
- let
- path =
- case model.id of
- Just id -> "/v" ++ String.fromInt id ++ "/edit"
- Nothing -> "/v/add"
- body = Gen.vneditSendEncode (encode model)
- in ({ model | state = Api.Loading }, Api.post path body Submitted)
-
- Submitted (Gen.Changed id rev) -> (model, load <| "/v" ++ String.fromInt id ++ "." ++ String.fromInt rev)
- Submitted r -> ({ model | state = Api.Error r }, Cmd.none)
-
- General m -> let (nm, c) = General.update m model.general in ({ model | general = nm }, Cmd.map General c)
- Staff m -> let (nm, c) = Staff.update m model.staff in ({ model | staff = nm }, Cmd.map Staff c)
- Seiyuu m -> let (nm, c) = Seiyuu.update m model.seiyuu in ({ model | seiyuu = nm }, Cmd.map Seiyuu c)
- Screenshots m -> let (nm, c) = Scr.update m model.screenshots in ({ model | screenshots = nm }, Cmd.map Screenshots c)
- Relations m -> let (nm, c) = Rel.update m model.relations in ({ model | relations = nm }, Cmd.map Relations c)
-
- CheckDup ->
- let body = JE.object
- [ ("search", JE.list JE.string <| List.filter ((/=)"") <| model.titles.title :: model.titles.original :: model.titles.aliasList)
- , ("hidden", JE.bool True) ]
- in
- if List.isEmpty model.dupVNs
- then ({ model | state = Api.Loading }, Api.post "/js/vn.json" body RecvDup)
- else ({ model | new = False }, Cmd.none)
-
- RecvDup (Gen.VNResult dup) ->
- ({ model | state = Api.Normal, dupVNs = dup, new = not (List.isEmpty dup) }, Cmd.none)
- RecvDup r -> ({ model | state = Api.Error r }, Cmd.none)
-
-
-
-isValid : Model -> Bool
-isValid model = not
- ( model.titles.aliasDuplicates
- || not (List.isEmpty model.titles.aliasBad)
- || model.general.animeDuplicates
- || model.staff.duplicates
- || model.seiyuu.duplicates
- || model.relations.duplicates
- )
-
-
-view : Model -> Html Msg
-view model =
- if model.new
- then form_ CheckDup (model.state == Api.Loading)
- [ card "new" "Add a new visual novel"
- [ div [class "card__subheading"]
- [ text "Carefully read the "
- , a [ href "/d2" ] [ text "guidelines" ]
- , text " before creating a new visual novel entry, to make sure that the game indeed conforms to our inclusion criteria."
- ]
- ] <|
- List.map (Html.map Titles) <| Titles.view model.titles
- , if List.isEmpty model.dupVNs
- then text ""
- else card "dup" "Possible duplicates" [ div [ class "card__subheading" ] [ text "Please check the list below for possible duplicates." ] ]
- [ cardRow "" Nothing <| formGroup [ div [ class "form-group__help" ] [
- ul [] <| List.map (\e ->
- li [] [ a [ href <| "/v" ++ String.fromInt e.id, title e.original, target "_black" ] [ text e.title ]
- , text <| if e.hidden then " (deleted)" else "" ]
- ) model.dupVNs
- ] ] ]
- , submitButton "Continue" model.state (isValid model) False
- ]
-
- else form_ Submit (model.state == Api.Loading)
- [ General.view model.general General <| List.map (Html.map Titles) <| Titles.view model.titles
- , Html.map Staff <| lazy Staff.view model.staff
- , Html.map Seiyuu <| lazy2 Seiyuu.view model.seiyuu model.id
- , Html.map Relations <| lazy Rel.view model.relations
- , Html.map Screenshots <| lazy2 Scr.view model.screenshots model.id
- , Html.map Editsum <| lazy Editsum.view model.editsum
- , submitButton "Submit" model.state (isValid model) (model.general.imgState == Api.Loading || Scr.loading model.screenshots)
- ]
diff --git a/elm3/VNEdit/New.elm b/elm3/VNEdit/New.elm
deleted file mode 100644
index eee9ade6..00000000
--- a/elm3/VNEdit/New.elm
+++ /dev/null
@@ -1,12 +0,0 @@
-module VNEdit.New exposing (main)
-
-import Browser
-import VNEdit.Main as Main
-
-main : Program () Main.Model Main.Msg
-main = Browser.element
- { init = always (Main.new, Cmd.none)
- , view = Main.view
- , update = Main.update
- , subscriptions = always Sub.none
- }
diff --git a/elm3/VNEdit/Relations.elm b/elm3/VNEdit/Relations.elm
deleted file mode 100644
index c9f82ec6..00000000
--- a/elm3/VNEdit/Relations.elm
+++ /dev/null
@@ -1,89 +0,0 @@
-module VNEdit.Relations exposing (Model, Msg, init, update, view)
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Html.Events exposing (..)
-import Lib.Html exposing (..)
-import Lib.Gen as Gen
-import Lib.Util exposing (..)
-import Lib.Autocomplete as A
-
-
-type alias Model =
- { relations : List Gen.VNEditRelations
- , search : A.Model Gen.ApiVNResult
- , duplicates : Bool
- }
-
-
-init : List Gen.VNEditRelations -> Model
-init l =
- { relations = l
- , search = A.init
- , duplicates = False
- }
-
-
-type Msg
- = Del Int
- | Official Int Bool
- | Rel Int String
- | Search (A.Msg Gen.ApiVNResult)
-
-
-searchConfig : A.Config Msg Gen.ApiVNResult
-searchConfig = { wrap = Search, id = "add-relation", source = A.vnSource }
-
-
-validate : Model -> Model
-validate model = { model | duplicates = hasDuplicates <| List.map .vid model.relations }
-
-
-update : Msg -> Model -> (Model, Cmd Msg)
-update msg model =
- case msg of
- Del i -> (validate { model | relations = delidx i model.relations }, Cmd.none)
- Official i b -> (validate { model | relations = modidx i (\e -> { e | official = b }) model.relations }, Cmd.none)
- Rel i s -> (validate { model | relations = modidx i (\e -> { e | relation = s }) model.relations }, Cmd.none)
- Search m ->
- let (nm, c, res) = A.update searchConfig m model.search
- in case res of
- Nothing -> ({ model | search = nm }, c)
- Just r ->
- let
- rel = List.head Gen.vnRelations |> Maybe.map Tuple.first |> Maybe.withDefault ""
- nrow = { vid = r.id, relation = rel, title = r.title, official = True }
- in (validate { model | search = A.clear nm, relations = model.relations ++ [nrow] }, c)
-
-
-view : Model -> Html Msg
-view model =
- let
- entry n e = editListRow "row--ai-center"
- [ editListField 1 "text-sm-right single-line"
- [ a [href <| "/v" ++ String.fromInt e.vid, title e.title, target "_blank" ] [text e.title ] ]
- , editListField 0 ""
- [ text "is an "
- , label [class "checkbox"]
- [ inputCheck "" e.official (Official n)
- , text " official"
- ]
- ]
- , editListField 1 ""
- [ inputSelect [onInput (Rel n)] e.relation Gen.vnRelations ]
- , editListField 0 "single-line" [ text " of this VN" ]
- , editListField 0 "" [ removeButton (Del n) ]
- ]
-
- in card "relations" "Relations" [] <|
- editList (List.indexedMap entry model.relations)
- ++ formGroups (
- (if model.duplicates
- then [ [ div [ class "invalid-feedback" ]
- [ text "The list contains duplicates. Make sure that the same visual novel is not listed multiple times." ] ] ]
- else []
- ) ++
- [ label [for "add-relation"] [text "Add relation"]
- :: A.view searchConfig model.search [placeholder "Visual Novel...", style "max-width" "400px"]
- ]
- )
diff --git a/elm3/VNEdit/Screenshots.elm b/elm3/VNEdit/Screenshots.elm
deleted file mode 100644
index f0888c8e..00000000
--- a/elm3/VNEdit/Screenshots.elm
+++ /dev/null
@@ -1,182 +0,0 @@
-module VNEdit.Screenshots exposing (Model, Msg, loading, init, update, view)
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Html.Events exposing (..)
-import File exposing (File)
-import Lib.Html exposing (..)
-import Lib.Util exposing (..)
-import Lib.Api as Api
-import Lib.Gen as Gen
-import Lib.Util exposing (lookup, isJust)
-
-
-type alias Model =
- { screenshots : List Gen.VNEditScreenshots
- , releases : List Gen.VNEditReleases
- , state : List Api.State
- , id : Int -- Temporary negative internal screenshot identifier, until the image has been uploaded and the actual ID is known
- , rel : Int
- , nsfw : Bool
- , files : List File
- }
-
-
-init : List Gen.VNEditScreenshots -> List Gen.VNEditReleases -> Model
-init scr rels =
- { screenshots = scr
- , releases = rels
- , state = List.map (always Api.Normal) scr
- , id = -1
- , rel = Maybe.withDefault 0 <| Maybe.map .id <| List.head rels
- , nsfw = False
- , files = []
- }
-
-
-loading : Model -> Bool
-loading model = List.any (\s -> s /= Api.Normal) model.state
-
-
-type Msg
- = Del Int
- | SetNSFW Int Bool
- | SetRel Int String
- | DefNSFW Bool
- | DefRel String
- | DefFiles (List File)
- | Upload
- | Done Int Api.Response
-
-
-update : Msg -> Model -> (Model, Cmd Msg)
-update msg model =
- case msg of
- Del i -> ({ model | screenshots = delidx i model.screenshots, state = delidx i model.state }, Cmd.none)
- SetNSFW i b -> ({ model | screenshots = modidx i (\e -> { e | nsfw = b }) model.screenshots }, Cmd.none)
- SetRel i s -> ({ model | screenshots = modidx i (\e -> { e | rid = Maybe.withDefault e.rid (String.toInt s) }) model.screenshots }, Cmd.none)
- DefNSFW b -> ({ model | nsfw = b }, Cmd.none)
- DefRel s -> ({ model | rel = Maybe.withDefault 0 (String.toInt s) }, Cmd.none)
- DefFiles l -> ({ model | files = l }, Cmd.none)
-
- Upload ->
- let
- st = model.state ++ List.map (always Api.Loading) model.files
- scr i _ = { scr = model.id - i, rid = model.rel, nsfw = model.nsfw, width = 0, height = 0 }
- alst = List.indexedMap scr model.files
- lst = model.screenshots ++ alst
- nid = model.id - List.length model.files
- cmd f i = Api.postImage Api.Sf f (Done i.scr)
- cmds = List.map2 cmd model.files alst
- in ({ model | screenshots = lst, id = nid, state = st, files = [] }, Cmd.batch cmds)
-
- Done id r ->
- case List.head <| List.filter (\(_,i) -> i.scr == id) <| List.indexedMap (\a b -> (a,b)) model.screenshots of
- Nothing -> (model, Cmd.none)
- Just (n,_) ->
- let
- st _ = case r of
- Gen.Image _ _ _ -> Api.Normal
- re -> Api.Error re
- scr s = case r of
- Gen.Image nid width height -> { s | scr = nid, width = width, height = height }
- _ -> s
- in ({ model | screenshots = modidx n scr model.screenshots, state = modidx n st model.state }, Cmd.none)
-
-
-
-view : Model -> Maybe Int -> Html Msg
-view model vid =
- let
- row image remove titl opts after = div [class "screenshot-edit__row"]
- [ div [ class "screenshot-edit__screenshot" ] [ image ]
- , div [ class "screenshot-edit__fields" ] <|
- [ remove
- , div [ class "screenshot-edit__title" ] [ text titl ]
- , div [ class "screenshot-edit__options" ] opts
- ] ++ after
- ]
-
- rm n = div [ class "screenshot-edit__remove" ] [ removeButton (Del n) ]
- img n f = dbImg "st" n [class "vn-image-placeholder--wide"] f
-
- commonRes res =
- -- NDS resolution, not in the database
- res == "256x384" || isJust (lookup res Gen.resolutions)
-
- resWarn e =
- let res = String.fromInt e.width ++ "x" ++ String.fromInt e.height
- in case List.filter (\r -> r.id == e.rid) model.releases |> List.head of
- Nothing -> text "" -- Shouldn't happen
- Just r ->
- -- If the release resolution is known and does *not* match the image resolution, warn about that
- if r.resolution /= "unknown" && r.resolution /= "nonstandard" && r.resolution /= res
- then div [ class "invalid-feedback" ]
- [ text <| "Screenshot resolution is not the same as that of the selected release (" ++ r.resolution ++ "). Please make sure take screenshots in that *exact* resolution!" ]
- -- Otherwise, if this isn't a non-standard resolution, check for common ones
- else if r.resolution == "nonstandard" || commonRes res
- then text ""
- else div [ class "invalid-feedback" ]
- [ text <| "Odd screenshot resolution. Please make sure take screenshots in the correct resolution!" ]
-
- entry n (s,e) = case s of
- Api.Loading -> row (img -1 Nothing) (rm n) "Uploading screenshot" [] []
- Api.Error r -> row
- (img 0 Nothing) (rm n) "Upload failed"
- [ div [ class "invalid-feedback" ] [ text <| Api.showResponse r ] ]
- []
- Api.Normal -> row
- (img e.scr <| Just { width = e.width, height = e.height, id = "scr" })
- (rm n) ("Screenshot #" ++ String.fromInt e.scr)
- [ span [ class "muted" ] [ text <| String.fromInt e.width ++ "x" ++ String.fromInt e.height ]
- , label [ class "checkbox" ]
- [ inputCheck "" e.nsfw (SetNSFW n)
- , text " Not safe for work"
- ]
- ]
- [ resWarn e
- , releaseSelect e.rid (SetRel n) ]
-
- add = if List.length model.screenshots == 10 then text "" else row
- (text "")
- (text "")
- "Add screenshot"
- [ span [ class "muted" ] [ text "Image must be smaller than 5MB and in PNG or JPEG format. No more than 10 screenshots can be uploaded." ] ]
- [ releaseSelect model.rel DefRel
- , div [ class "screenshot-edit__upload-options" ]
- [ div [ class "screenshot-edit__upload-option" ] [ input [ type_ "file", id "addscr", tabindex 10, multiple True, Api.onFileChange DefFiles ] [] ]
- , div [ class "screenshot-edit__upload-option" ]
- [ label [ class "checkbox screenshot-edit__upload-nsfw-label" ]
- [ inputCheck "" model.nsfw DefNSFW
- , text " Not safe for work" ] ]
- , div [ class "flex-expand" ] []
- , div [ class "screenshot-edit__upload-option" ]
- [ button
- [ type_ "button", class "btn screenshot-edit__upload-btn", tabindex 10, onClick Upload
- , disabled <| List.isEmpty model.files || (List.length model.files + List.length model.screenshots) > 10
- ] [ text "Upload!" ] ]
- ]
- ]
-
- releaseSelect rid msg = inputSelect [onInput msg] (String.fromInt rid)
- <| List.map (\s -> (String.fromInt s.id, s.display)) model.releases
-
- norel =
- case vid of
- Nothing -> [ text "Screenshots can be uploaded after adding releases to this visual novel." ]
- Just i ->
- [ text "Screenshots can be added after "
- , a [ href <| "/v" ++ (String.fromInt i) ++ "/add", target "_blank" ] [ text "adding a release entry" ]
- , text "."
- ]
-
- in if List.isEmpty model.releases
- then card "screenshots" "Screenshots" [ div [class "card__subheading"] norel ] []
- else card "screenshots" "Screenshots"
- [ div [class "card__subheading"]
- [ text "Keep in mind that all screenshots must conform to "
- , a [href "/d2#6", target "blank"] [ text "strict guidelines" ]
- , text ", read those carefully!"
- ]
- ]
- [ div [class "screenshot-edit"] <| List.indexedMap entry (List.map2 (\a b -> (a,b)) model.state model.screenshots) ++ [ add ] ]
diff --git a/elm3/VNEdit/Seiyuu.elm b/elm3/VNEdit/Seiyuu.elm
deleted file mode 100644
index 3473bb7b..00000000
--- a/elm3/VNEdit/Seiyuu.elm
+++ /dev/null
@@ -1,104 +0,0 @@
-module VNEdit.Seiyuu exposing (Model, Msg, init, update, view)
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Html.Events exposing (..)
-import Lib.Util exposing (..)
-import Lib.Html exposing (..)
-import Lib.Gen as Gen
-import Lib.Autocomplete as A
-
-
-type alias Model =
- { chars : List Gen.VNEditChars
- , seiyuu : List Gen.VNEditSeiyuu
- , search : A.Model Gen.ApiStaffResult
- , duplicates : Bool
- }
-
-
-init : List Gen.VNEditSeiyuu -> List Gen.VNEditChars -> Model
-init s c =
- { chars = c
- , seiyuu = s
- , search = A.init
- , duplicates = False
- }
-
-
-type Msg
- = Del Int
- | SetNote Int String
- | SetChar Int String
- | Search (A.Msg Gen.ApiStaffResult)
-
-
-searchConfig : A.Config Msg Gen.ApiStaffResult
-searchConfig = { wrap = Search, id = "add-seiyuu", source = A.staffSource }
-
-
-validate : Model -> Model
-validate model = { model | duplicates = hasDuplicates <| List.map (\e -> (e.aid,e.cid )) model.seiyuu }
-
-
-update : Msg -> Model -> (Model, Cmd Msg)
-update msg model =
- case msg of
- Del i -> (validate { model | seiyuu = delidx i model.seiyuu }, Cmd.none)
- SetNote i s -> (validate { model | seiyuu = modidx i (\e -> { e | note = s }) model.seiyuu }, Cmd.none)
- SetChar i s -> (validate { model | seiyuu = modidx i (\e -> { e | cid = Maybe.withDefault e.cid (String.toInt s) }) model.seiyuu }, Cmd.none)
-
- Search m ->
- let (nm, c, res) = A.update searchConfig m model.search
- in case res of
- Nothing -> ({ model | search = nm }, c)
- Just r ->
- let
- char = List.head model.chars |> Maybe.map .id |> Maybe.withDefault 0
- nrow = { aid = r.aid, cid = char, id = r.id, name = r.name, note = "" }
- nmod = { model | search = A.clear nm, seiyuu = model.seiyuu ++ [nrow] }
- in (validate nmod, c)
-
-
-
-view : Model -> Maybe Int -> Html Msg
-view model id =
- let
- entry n e = editListRow ""
- [ editListField 1 "col-form-label single-line"
- [ a [href <| "/s" ++ String.fromInt e.id, target "_blank" ] [ text e.name ] ]
- , editListField 1 ""
- [ inputSelect
- [onInput (SetChar n)]
- (String.fromInt e.cid)
- (List.map (\c -> (String.fromInt c.id, c.name)) model.chars)
- ]
- , editListField 2 "" [ inputText "" e.note (SetNote n) [placeholder "Note", maxlength 250] ]
- , editListField 0 "" [ removeButton (Del n) ]
- ]
-
- nochars =
- case id of
- Nothing -> [ text "Cast can be added when the visual novel entry has characters linked to it." ]
- Just n ->
- [ text "Cast can be added after "
- , a [ href <| "/c/new?vid=" ++ (String.fromInt n), target "_blank" ] [ text "creating" ]
- , text " the appropriate character entries, or after linking "
- , a [ href "/c/all" ] [ text "existing characters" ]
- , text " to this visual novel entry."
- ]
-
- in if List.isEmpty model.chars
- then card "cast" "Cast" [ div [class "card__subheading"] nochars ] []
- else card "cast" "Cast" [] <|
- editList (List.indexedMap entry model.seiyuu)
- ++ formGroups (
- (if model.duplicates
- then [ [ div [ class "invalid-feedback" ]
- [ text "The cast list contains duplicates. Make sure that each person is only listed at most once for the same character" ] ] ]
- else []
- ) ++
- [ label [for "add-seiyuu"] [text "Add cast"]
- :: A.view searchConfig model.search [placeholder "Cast name", style "max-width" "400px"]
- ]
- )
diff --git a/elm3/VNEdit/Staff.elm b/elm3/VNEdit/Staff.elm
deleted file mode 100644
index 595b1eb0..00000000
--- a/elm3/VNEdit/Staff.elm
+++ /dev/null
@@ -1,95 +0,0 @@
-module VNEdit.Staff exposing (Model, Msg, init, update, view)
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Html.Events exposing (..)
-import Lib.Html exposing (..)
-import Lib.Autocomplete as A
-import Lib.Gen as Gen
-import Lib.Util exposing (..)
-
-
-type alias Model =
- { staff : List Gen.VNEditStaff
- , search : A.Model Gen.ApiStaffResult
- , duplicates : Bool
- }
-
-
-init : List Gen.VNEditStaff -> Model
-init l =
- { staff = l
- , search = A.init
- , duplicates = False
- }
-
-
-type Msg
- = Del Int
- | SetNote Int String
- | SetRole Int String
- | Search (A.Msg Gen.ApiStaffResult)
-
-
-searchConfig : A.Config Msg Gen.ApiStaffResult
-searchConfig = { wrap = Search, id = "add-staff", source = A.staffSource }
-
-
-validate : Model -> Model
-validate model = { model | duplicates = hasDuplicates <| List.map (\e -> (e.aid,e.role)) model.staff }
-
-
-update : Msg -> Model -> (Model, Cmd Msg)
-update msg model =
- case msg of
- Del i -> (validate { model | staff = delidx i model.staff }, Cmd.none)
- SetNote i s -> (validate { model | staff = modidx i (\e -> { e | note = s }) model.staff }, Cmd.none)
- SetRole i s -> (validate { model | staff = modidx i (\e -> { e | role = s }) model.staff }, Cmd.none)
-
- Search m ->
- let (nm, c, res) = A.update searchConfig m model.search
- in case res of
- Nothing -> ({ model | search = nm }, c)
- Just r ->
- let
- role = List.head Gen.creditType |> Maybe.map Tuple.first |> Maybe.withDefault ""
- nrow = { aid = r.aid, id = r.id, name = r.name, role = role, note = "" }
- in (validate { model | search = A.clear nm, staff = model.staff ++ [nrow] }, c)
-
-
-
-view : Model -> Html Msg
-view model =
- let
- entry n e = editListRow ""
- [ editListField 1 "col-form-label single-line"
- [ a [href <| "/s" ++ String.fromInt e.id, target "_blank" ] [text e.name ] ]
- , editListField 1 ""
- [ inputSelect [onInput (SetRole n)] e.role Gen.creditType ]
- , editListField 2 ""
- [ inputText "" e.note (SetNote n) [placeholder "Note", maxlength 250] ]
- , editListField 0 "" [ removeButton (Del n) ]
- ]
-
- in card "staff" "Staff"
- [ div [class "card__subheading"]
- [ text "For information, check the "
- , a [href "/d2#3", target "_blank"] [text "staff editing guidelines"]
- , text ". You can "
- , a [href "/s/new", target "_blank"] [text "create a new staff entry"]
- , text " if it is not in the database yet, but please "
- , a [href "/s/all", target "_blank"] [text "check for aliases first"]
- , text "."
- ]
- ] <|
- editList (List.indexedMap entry model.staff)
- ++ formGroups (
- (if model.duplicates
- then [ [ div [ class "invalid-feedback" ]
- [ text "The staff list contains duplicates. Make sure that each person is only listed at most once with the same role" ] ] ]
- else []
- ) ++
- [ label [for "add-staff"] [text "Add staff"]
- :: A.view searchConfig model.search [placeholder "Staff name", style "max-width" "400px"]
- ]
- )
diff --git a/elm3/VNEdit/Titles.elm b/elm3/VNEdit/Titles.elm
deleted file mode 100644
index 9dad830d..00000000
--- a/elm3/VNEdit/Titles.elm
+++ /dev/null
@@ -1,103 +0,0 @@
-module VNEdit.Titles exposing (..)
-
-import Html exposing (..)
-import Html.Attributes exposing (..)
-import Dict
-import Lib.Html exposing (..)
-import Lib.Gen exposing (..)
-import Lib.Util exposing (..)
-
-
-type alias Model =
- { title : String
- , original : String
- , alias : String
- , aliasList : List String
- , aliasDuplicates : Bool
- , aliasBad : List String
- , aliasRel : Dict.Dict String Bool
- }
-
-
-init : VNEdit -> Model
-init d =
- { title = d.title
- , original = d.original
- , alias = d.alias
- , aliasList = splitLn d.alias
- , aliasDuplicates = False
- , aliasBad = []
- , aliasRel = Dict.fromList <| List.map (\e -> (e,True)) <| List.map .title d.releases ++ List.map .original d.releases
- }
-
-
-new : Model
-new =
- { title = ""
- , original = ""
- , alias = ""
- , aliasList = []
- , aliasDuplicates = False
- , aliasBad = []
- , aliasRel = Dict.empty
- }
-
-
-type Msg
- = Title String
- | Original String
- | Alias String
-
-
-update : Msg -> Model -> Model
-update msg model =
- case msg of
- Title s -> { model | title = s }
- Original s -> { model | original = s }
- Alias s ->
- let
- lst = splitLn s
- check a = a == model.title || a == model.original || Dict.member a model.aliasRel
- in
- { model
- | alias = s
- , aliasList = lst
- , aliasDuplicates = hasDuplicates lst
- , aliasBad = List.filter check lst
- }
-
-
-view : Model -> List (Html Msg)
-view model =
- [ cardRow "Title" Nothing <| formGroups
- [ [ label [for "title"] [text "Title (romaji)"]
- , inputText "title" model.title Title [required True, maxlength 250]
- ]
- , [ label [for "original"] [text "Original"]
- , inputText "original" model.original Original [maxlength 250]
- , div [class "form-group__help"] [text "The original title of this visual novel, leave blank if it already is in the Latin alphabet."]
- ]
- ]
- , cardRow "Aliases" Nothing <| formGroup
- [ inputTextArea "aliases" model.alias Alias
- [ rows 4, maxlength 500
- , classList [("is-invalid", model.aliasDuplicates || not (List.isEmpty model.aliasBad))]
- ]
- , if model.aliasDuplicates
- then div [class "invalid-feedback"]
- [ text "There are duplicate aliases." ]
- else text ""
- , if List.isEmpty model.aliasBad
- then text ""
- else div [class "invalid-feedback"]
- [ text
- <| "The following aliases are already listed elsewhere and should be removed: "
- ++ String.join ", " model.aliasBad
- ]
- , div [class "form-group__help"]
- [ text "List of alternative titles or abbreviations. One line for each alias. Can include both official (japanese/english) titles and unofficial titles used around net."
- , br [] []
- , text "Titles that are listed in the releases should not be added here!"
- ]
- ]
- ]
diff --git a/elm3/elm.json b/elm3/elm.json
deleted file mode 100644
index 6867e2b1..00000000
--- a/elm3/elm.json
+++ /dev/null
@@ -1,30 +0,0 @@
-{
- "type": "application",
- "source-directories": [
- "."
- ],
- "elm-version": "0.19.1",
- "dependencies": {
- "direct": {
- "elm/browser": "1.0.1",
- "elm/core": "1.0.2",
- "elm/file": "1.0.1",
- "elm/html": "1.0.0",
- "elm/http": "2.0.0",
- "elm/json": "1.1.2",
- "elm/regex": "1.0.0",
- "justinmimbs/date": "3.1.2"
- },
- "indirect": {
- "elm/bytes": "1.0.3",
- "elm/parser": "1.1.0",
- "elm/time": "1.0.0",
- "elm/url": "1.0.0",
- "elm/virtual-dom": "1.0.2"
- }
- },
- "test-dependencies": {
- "direct": {},
- "indirect": {}
- }
-}