summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYorhel <git@yorhel.nl>2020-05-12 10:25:02 +0200
committerYorhel <git@yorhel.nl>2020-05-12 10:25:02 +0200
commit13d14259b08ab9d40f14bfc3edabb816e3ea8d20 (patch)
tree973e82be525997b212bd2f495af0731b57cd3c2d
parentc18d0d12829c4c42910d66a4d7fbb035ba8dc7ac (diff)
Char::Edit: Initial rewrite of the character edit form, general info first
-rw-r--r--data/style.css2
-rw-r--r--elm/CharEdit.elm202
-rw-r--r--elm/Discussions/Edit.elm6
-rw-r--r--elm/Lib/Html.elm6
-rw-r--r--lib/VNDB/Handler/Chars.pm2
-rw-r--r--lib/VNWeb/Chars/Edit.pm81
-rw-r--r--lib/VNWeb/Elm.pm15
7 files changed, 301 insertions, 13 deletions
diff --git a/data/style.css b/data/style.css
index 9c2fbf0e..2199bdd9 100644
--- a/data/style.css
+++ b/data/style.css
@@ -168,6 +168,8 @@ legend { display: none; }
optgroup option { padding-left: 10px; font-style: normal; }
input.submit { background: $boxbg$; padding: 1px 5px; }
input.text, select { width: 200px; }
+input[type=number] { -moz-appearance:textfield }
+input[type=number]::-webkit-outer-spin-button, input[type=number]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0 }
fieldset.submit { width: 100%; text-align: center; margin: 5px; }
fieldset.submit input[type=submit] { width: 150px; }
fieldset.submit input[type=checkbox] { margin: 0 5px 0 15px; }
diff --git a/elm/CharEdit.elm b/elm/CharEdit.elm
new file mode 100644
index 00000000..75958e80
--- /dev/null
+++ b/elm/CharEdit.elm
@@ -0,0 +1,202 @@
+module CharEdit exposing (main)
+
+import Html exposing (..)
+import Html.Events exposing (..)
+import Html.Attributes exposing (..)
+import Browser
+import Browser.Navigation exposing (load)
+import Lib.Util exposing (..)
+import Lib.Html exposing (..)
+import Lib.TextPreview as TP
+import Lib.Api as Api
+import Lib.Editsum as Editsum
+import Gen.CharEdit as GCE
+import Gen.Types as GT
+import Gen.Api as GApi
+
+
+main : Program GCE.Recv 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
+ , name : String
+ , original : String
+ , alias : String
+ , desc : TP.Model
+ , gender : String
+ , bMonth : Int
+ , bDay : Int
+ , age : Maybe Int
+ , sBust : Int
+ , sWaist : Int
+ , sHip : Int
+ , height : Int
+ , weight : Maybe Int
+ , bloodt : String
+ , cupSize : String
+ , id : Maybe Int
+ }
+
+
+init : GCE.Recv -> Model
+init d =
+ { state = Api.Normal
+ , editsum = { authmod = d.authmod, editsum = TP.bbcode d.editsum, locked = d.locked, hidden = d.hidden }
+ , name = d.name
+ , original = d.original
+ , alias = d.alias
+ , desc = TP.bbcode d.desc
+ , gender = d.gender
+ , bMonth = d.b_month
+ , bDay = if d.b_day == 0 then 1 else d.b_day
+ , age = d.age
+ , sBust = d.s_bust
+ , sWaist = d.s_waist
+ , sHip = d.s_hip
+ , height = d.height
+ , weight = d.weight
+ , bloodt = d.bloodt
+ , cupSize = d.cup_size
+ , id = d.id
+ }
+
+
+encode : Model -> GCE.Send
+encode model =
+ { id = model.id
+ , editsum = model.editsum.editsum.data
+ , hidden = model.editsum.hidden
+ , locked = model.editsum.locked
+ , name = model.name
+ , original = model.original
+ , alias = model.alias
+ , desc = model.desc.data
+ , gender = model.gender
+ , b_month = model.bMonth
+ , b_day = model.bDay
+ , age = model.age
+ , s_bust = model.sBust
+ , s_waist = model.sWaist
+ , s_hip = model.sHip
+ , height = model.height
+ , weight = model.weight
+ , bloodt = model.bloodt
+ , cup_size = model.cupSize
+ }
+
+
+type Msg
+ = Editsum Editsum.Msg
+ | Submit
+ | Submitted GApi.Response
+ | Name String
+ | Original String
+ | Alias String
+ | Desc TP.Msg
+ | Gender String
+ | BMonth Int
+ | BDay Int
+ | Age (Maybe Int)
+ | SBust (Maybe Int)
+ | SWaist (Maybe Int)
+ | SHip (Maybe Int)
+ | Height (Maybe Int)
+ | Weight (Maybe Int)
+ | BloodT String
+ | CupSize String
+
+
+update : Msg -> Model -> (Model, Cmd Msg)
+update msg model =
+ case msg of
+ Editsum m -> let (nm,nc) = Editsum.update m model.editsum in ({ model | editsum = nm }, Cmd.map Editsum nc)
+ Name s -> ({ model | name = s }, Cmd.none)
+ Original s -> ({ model | original = s }, Cmd.none)
+ Alias s -> ({ model | alias = s }, Cmd.none)
+ Desc m -> let (nm,nc) = TP.update m model.desc in ({ model | desc = nm }, Cmd.map Desc nc)
+ Gender s -> ({ model | gender = s }, Cmd.none)
+ BMonth n -> ({ model | bMonth = n }, Cmd.none)
+ BDay n -> ({ model | bDay = n }, Cmd.none)
+ Age s -> ({ model | age = s }, Cmd.none)
+ SBust s -> ({ model | sBust = Maybe.withDefault 0 s }, Cmd.none)
+ SWaist s -> ({ model | sWaist = Maybe.withDefault 0 s }, Cmd.none)
+ SHip s -> ({ model | sHip = Maybe.withDefault 0 s }, Cmd.none)
+ Height s -> ({ model | height = Maybe.withDefault 0 s }, Cmd.none)
+ Weight s -> ({ model | weight = s }, Cmd.none)
+ BloodT s -> ({ model | bloodt = s }, Cmd.none)
+ CupSize s -> ({ model | cupSize= s }, Cmd.none)
+
+ Submit -> ({ model | state = Api.Loading }, GCE.send (encode model) Submitted)
+ Submitted (GApi.Redirect s) -> (model, load s)
+ Submitted r -> ({ model | state = Api.Error r }, Cmd.none)
+
+
+isValid : Model -> Bool
+isValid model = not
+ ( model.name == model.original
+ )
+
+
+view : Model -> Html Msg
+view model =
+ form_ Submit (model.state == Api.Loading)
+ [ div [ class "mainbox" ]
+ [ h1 [] [ text "General info" ]
+ , table [ class "formtable" ]
+ [ formField "name::Name (romaji)" [ inputText "name" model.name Name GCE.valName ]
+ , formField "original::Original name"
+ [ inputText "original" model.original Original GCE.valOriginal
+ , if model.name /= "" && model.name == model.original
+ then b [ class "standout" ] [ br [] [], text "Should not be the same as the Name (romaji). Leave blank is the original name is already in the latin alphabet" ]
+ else text ""
+ ]
+ , formField "alias::Aliases"
+ [ inputTextArea "alias" model.alias Alias (rows 3 :: GCE.valAlias)
+ , br [] []
+ , text "(Un)official aliases, separated by a newline. Must not include spoilers!"
+ ]
+ , formField "desc::Description" [ TP.view "desc" model.desc Desc 600 (style "height" "150px" :: GCE.valDesc) [ b [ class "standout" ] [ text "English please!" ] ] ]
+ , formField "gender::Sex" [ inputSelect "gender" model.gender Gender [] GT.genders ]
+ , formField "bmonth::Birthday"
+ [ inputSelect "bmonth" model.bMonth BMonth [style "width" "128px"]
+ [ ( 0, "Unknown")
+ , ( 1, "January")
+ , ( 2, "February")
+ , ( 3, "March")
+ , ( 4, "April")
+ , ( 5, "May")
+ , ( 6, "June")
+ , ( 7, "July")
+ , ( 8, "August")
+ , ( 9, "September")
+ , (10, "October")
+ , (11, "November")
+ , (12, "December")
+ ]
+ , if model.bMonth == 0 then text ""
+ else inputSelect "" model.bDay BDay [style "width" "70px"] <| List.map (\i -> (i, String.fromInt i)) <| List.range 1 31
+ ]
+ , formField "age::Age" [ inputNumber "age" model.age Age GCE.valAge, text " years" ]
+ , formField "sbust::Bust" [ inputNumber "sbust" (if model.sBust == 0 then Nothing else Just model.sBust ) SBust GCE.valS_Bust, text " cm" ]
+ , formField "swaist::Waist" [ inputNumber "swiast" (if model.sWaist == 0 then Nothing else Just model.sWaist) SWaist GCE.valS_Waist,text " cm" ]
+ , formField "ship::Hips" [ inputNumber "ship" (if model.sHip == 0 then Nothing else Just model.sHip ) SHip GCE.valS_Hip, text " cm" ]
+ , formField "height::Height" [ inputNumber "height" (if model.height == 0 then Nothing else Just model.height) Height GCE.valHeight, text " cm" ]
+ , formField "weight::Weight" [ inputNumber "weight" model.weight Weight GCE.valWeight, text " kg" ]
+ , formField "bloodt::Blood type" [ inputSelect "bloodt" model.bloodt BloodT [] GT.bloodTypes ]
+ , formField "cupsize::Cup size" [ inputSelect "cupsize" model.cupSize CupSize [] GT.cupSizes ]
+ ]
+ ]
+ , div [ class "mainbox" ] [ fieldset [ class "submit" ]
+ [ Html.map Editsum (Editsum.view model.editsum)
+ , submitButton "Submit" model.state (isValid model)
+ ]
+ ]
+ ]
diff --git a/elm/Discussions/Edit.elm b/elm/Discussions/Edit.elm
index 84aad148..f8873fa7 100644
--- a/elm/Discussions/Edit.elm
+++ b/elm/Discussions/Edit.elm
@@ -108,7 +108,7 @@ type Msg
| BoardSearch (A.Msg GApi.ApiBoardResult)
| PollEnabled Bool
| PollQ String
- | PollMax Int
+ | PollMax (Maybe Int)
| PollOpt Int String
| PollRem Int
| PollAdd
@@ -128,7 +128,7 @@ update msg model =
Title s -> ({ model | title = Just s }, Cmd.none)
PollEnabled b -> ({ model | pollEnabled = b, poll = if model.poll == Nothing then Just { question = "", max_options = 1, options = ["",""] } else model.poll }, Cmd.none)
PollQ s -> ({ model | poll = Maybe.map (\p -> { p | question = s}) model.poll }, Cmd.none)
- PollMax n -> ({ model | poll = Maybe.map (\p -> { p | max_options = n}) model.poll }, Cmd.none)
+ PollMax n -> ({ model | poll = Maybe.map (\p -> { p | max_options = Maybe.withDefault 0 n}) model.poll }, Cmd.none)
PollOpt n s -> ({ model | poll = Maybe.map (\p -> { p | options = modidx n (always s) p.options }) model.poll }, Cmd.none)
PollRem n -> ({ model | poll = Maybe.map (\p -> { p | options = delidx n p.options }) model.poll }, Cmd.none)
PollAdd -> ({ model | poll = Maybe.map (\p -> { p | options = p.options ++ [""] }) model.poll }, Cmd.none)
@@ -201,7 +201,7 @@ view model =
else text ""
]
, formField ""
- [ inputNumber "" p.max_options PollMax <| GDE.valPollMax_Options ++ [ Html.Attributes.max <| String.fromInt <| List.length p.options ]
+ [ inputNumber "" (Just p.max_options) PollMax <| GDE.valPollMax_Options ++ [ Html.Attributes.max <| String.fromInt <| List.length p.options ]
, text " Number of options people are allowed to choose."
]
]
diff --git a/elm/Lib/Html.elm b/elm/Lib/Html.elm
index 2e534246..2d7d516c 100644
--- a/elm/Lib/Html.elm
+++ b/elm/Lib/Html.elm
@@ -79,14 +79,14 @@ inputSelect nam sel onch attrs lst =
) <| List.indexedMap opt lst
-inputNumber : String -> Int -> (Int -> m) -> List (Attribute m) -> Html m
+inputNumber : String -> Maybe Int -> (Maybe Int -> m) -> List (Attribute m) -> Html m
inputNumber nam val onch attrs = input (
[ type_ "number"
, class "text"
, tabindex 10
, style "width" "40px"
- , value <| String.fromInt val
- , onInput (\s -> onch <| Maybe.withDefault 0 <| String.toInt s)
+ , value <| Maybe.withDefault "" <| Maybe.map String.fromInt val
+ , onInput (\s -> onch <| String.toInt s)
]
++ attrs
++ (if nam == "" then [] else [ id nam, name nam ])
diff --git a/lib/VNDB/Handler/Chars.pm b/lib/VNDB/Handler/Chars.pm
index ee2452f9..781a5507 100644
--- a/lib/VNDB/Handler/Chars.pm
+++ b/lib/VNDB/Handler/Chars.pm
@@ -11,7 +11,7 @@ use VNDB::Types;
our @EXPORT = ('charBrowseTable');
TUWF::register(
- qr{c(?:([1-9]\d*)(?:\.([1-9]\d*))?/(edit|copy)|/new)}
+ qr{old/c(?:([1-9]\d*)(?:\.([1-9]\d*))?/(edit|copy)|/new)}
=> \&edit,
qr{c/([a-z0]|all)} => \&list,
);
diff --git a/lib/VNWeb/Chars/Edit.pm b/lib/VNWeb/Chars/Edit.pm
new file mode 100644
index 00000000..4c19afce
--- /dev/null
+++ b/lib/VNWeb/Chars/Edit.pm
@@ -0,0 +1,81 @@
+package VNWeb::Chars::Edit;
+
+use VNWeb::Prelude;
+
+
+my $FORM = {
+ id => { required => 0, id => 1 },
+ name => { maxlength => 200 },
+ original => { required => 0, default => '', maxlength => 200 },
+ alias => { required => 0, default => '', maxlength => 500 },
+ desc => { required => 0, default => '', maxlength => 5000 },
+ gender => { default => 'unknown', enum => \%GENDER },
+ b_month => { required => 0, default => 0, uint => 1, range => [ 0, 12 ] },
+ b_day => { required => 0, default => 0, uint => 1, range => [ 0, 31 ] },
+ age => { required => 0, uint => 1, range => [ 0, 32767 ] },
+ s_bust => { required => 0, uint => 1, range => [ 0, 32767 ], default => 0 },
+ s_waist => { required => 0, uint => 1, range => [ 0, 32767 ], default => 0 },
+ s_hip => { required => 0, uint => 1, range => [ 0, 32767 ], default => 0 },
+ height => { required => 0, uint => 1, range => [ 0, 32767 ], default => 0 },
+ weight => { required => 0, uint => 1, range => [ 0, 32767 ] },
+ bloodt => { default => 'unknown', enum => \%BLOOD_TYPE },
+ cup_size => { required => 0, default => '', enum => \%CUP_SIZE },
+ hidden => { anybool => 1 },
+ locked => { anybool => 1 },
+
+ authmod => { _when => 'out', anybool => 1 },
+ editsum => { _when => 'in out', editsum => 1 },
+};
+
+my $FORM_OUT = form_compile out => $FORM;
+my $FORM_IN = form_compile in => $FORM;
+my $FORM_CMP = form_compile cmp => $FORM;
+
+
+TUWF::get qr{/$RE{crev}/edit} => sub {
+ my $e = db_entry c => tuwf->capture('id'), tuwf->capture('rev') or return tuwf->resNotFound;
+ return tuwf->resDenied if !can_edit c => $e;
+
+ $e->{authmod} = auth->permDbmod;
+ $e->{editsum} = $e->{chrev} == $e->{maxrev} ? '' : "Reverted to revision c$e->{id}.$e->{chrev}";
+
+ framework_ title => "Edit $e->{name}", type => 'c', dbobj => $e, tab => 'edit',
+ sub {
+ editmsg_ c => $e, "Edit $e->{name}";
+ elm_ CharEdit => $FORM_OUT, $e;
+ };
+};
+
+
+# XXX: Require VN
+TUWF::get qr{/c/new}, sub {
+ return tuwf->resDenied if !can_edit c => undef;
+ framework_ title => 'Add character',
+ sub {
+ editmsg_ c => undef, 'Add character';
+ elm_ CharEdit => $FORM_OUT, {
+ elm_empty($FORM_OUT)->%*,
+ };
+ };
+};
+
+
+elm_api CharEdit => $FORM_OUT, $FORM_IN, sub {
+ my $data = shift;
+ my $new = !$data->{id};
+ my $e = $new ? { id => 0 } : db_entry c => $data->{id} or return tuwf->resNotFound;
+ return elm_Unauth if !can_edit c => $e;
+
+ if(!auth->permDbmod) {
+ $data->{hidden} = $e->{hidden}||0;
+ $data->{locked} = $e->{locked}||0;
+ }
+ $data->{desc} = bb_subst_links $data->{desc};
+ $data->{b_day} = 0 if !$data->{b_month};
+
+ return elm_Unchanged if !$new && !form_changed $FORM_CMP, $data, $e;
+ my($id,undef,$rev) = db_edit c => $e->{id}, $data;
+ elm_Redirect "/c$id.$rev";
+};
+
+1;
diff --git a/lib/VNWeb/Elm.pm b/lib/VNWeb/Elm.pm
index 5d287851..247f21b7 100644
--- a/lib/VNWeb/Elm.pm
+++ b/lib/VNWeb/Elm.pm
@@ -172,12 +172,12 @@ sub def_validation {
my %v = $obj->html5_validation();
$data .= def $name, 'List (Html.Attribute msg)', '[ '.join(', ',
- $v{required} ? 'A.required True' : (),
- $v{minlength} ? "A.minlength $v{minlength}" : (),
- $v{maxlength} ? "A.maxlength $v{maxlength}" : (),
- $v{min} ? 'A.min '.string($v{min}) : (),
- $v{max} ? 'A.max '.string($v{max}) : (),
- $v{pattern} ? 'A.pattern '.string($v{pattern}) : ()
+ $v{required} ? 'A.required True' : (),
+ defined $v{minlength} ? "A.minlength $v{minlength}" : (),
+ defined $v{maxlength} ? "A.maxlength $v{maxlength}" : (),
+ defined $v{min} ? 'A.min '.string($v{min}) : (),
+ defined $v{max} ? 'A.max '.string($v{max}) : (),
+ $v{pattern} ? 'A.pattern '.string($v{pattern}) : ()
).']' if !$obj->{keys};
$data;
}
@@ -367,6 +367,9 @@ sub write_types {
$data .= def resolutions=> 'List (String, String)' => list map tuple(string $_, string +($RESOLUTION{$_}{cat}?"$RESOLUTION{$_}{cat} / ":'').$RESOLUTION{$_}{txt}), keys %RESOLUTION;
$data .= def voiced => 'List (Int, String)' => list map tuple($_, string $VOICED{$_}{txt}), keys %VOICED;
$data .= def animated => 'List (Int, String)' => list map tuple($_, string $ANIMATED{$_}{txt}), keys %ANIMATED;
+ $data .= def genders => 'List (String, String)' => list map tuple(string $_, string $GENDER{$_}), keys %GENDER;
+ $data .= def cupSizes => 'List (String, String)' => list map tuple(string $_, string $CUP_SIZE{$_}), keys %CUP_SIZE;
+ $data .= def bloodTypes => 'List (String, String)' => list map tuple(string $_, string $BLOOD_TYPE{$_}), keys %BLOOD_TYPE;
$data .= def curYear => Int => (gmtime)[5]+1900;
write_module Types => $data;