Skip to content

Commit 1ed764a

Browse files
committed
Allow user to set App Name
- Add XML Parser TagSoup to set the App Name in `config.xml` - Add UI components to receive App Name input
1 parent 6ba94af commit 1ed764a

File tree

7 files changed

+107
-22
lines changed

7 files changed

+107
-22
lines changed

codeworld-server/codeworld-server.cabal

+3
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Executable codeworld-server
2121
base64-bytestring,
2222
bytestring,
2323
codeworld-compiler,
24+
containers,
2425
cryptonite,
2526
data-default,
2627
directory,
@@ -35,6 +36,8 @@ Executable codeworld-server
3536
regex-tdfa,
3637
snap-core,
3738
snap-server,
39+
strict,
40+
tagsoup,
3841
temporary,
3942
text,
4043
unix

codeworld-server/src/AndroidExport.hs

+39-15
Original file line numberDiff line numberDiff line change
@@ -18,38 +18,62 @@
1818

1919
module AndroidExport where
2020

21-
import System.Process
22-
import System.Directory
23-
import System.FilePath
21+
import Data.Maybe
22+
import qualified Data.Map as M
23+
import System.Process
24+
import System.Directory
25+
import System.FilePath
26+
import qualified System.IO.Strict as ST
27+
import Text.HTML.TagSoup
2428

2529
import Util
2630

27-
buildAndroid :: BuildMode -> ProgramId -> IO()
28-
buildAndroid mode programId = do
31+
buildAndroid :: BuildMode -> ProgramId -> AppProps -> IO ()
32+
buildAndroid mode programId appProps = do
33+
let appName = fromJust $ M.lookup "appName" appProps
2934
initCordovaProject mode programId
3035
copySource mode programId
36+
setAppName mode programId appName
3137
buildApk mode programId
3238
return ()
3339

34-
3540
initCordovaProject :: BuildMode -> ProgramId -> IO ()
3641
initCordovaProject mode programId = do
42+
let rootDir = androidRootDir mode
43+
checkIfRootExists <- doesDirectoryExist rootDir
44+
if not checkIfRootExists
45+
then do
46+
createDirectory $ androidRootDir mode
47+
else return ()
3748
let buildDir = androidBuildDir mode programId
3849
checkIfBuildExists <- doesDirectoryExist buildDir
39-
case checkIfBuildExists of
40-
True -> return ()
41-
False -> do
50+
if not checkIfBuildExists
51+
then do
4252
checkIfParentExists <- doesDirectoryExist $ androidRootDir mode </> sourceParent programId
43-
case checkIfParentExists of
44-
True -> return ()
45-
False -> do
53+
if not checkIfParentExists
54+
then do
4655
createDirectory $ androidRootDir mode </> sourceParent programId
4756
copyDirIfExists "android-template" (androidRootDir mode </> sourceBase programId)
48-
return ()
57+
else return ()
58+
else return ()
4959

5060
copySource :: BuildMode -> ProgramId -> IO ()
51-
copySource mode programId = do
52-
copyFile (buildRootDir mode </> targetFile programId) (androidBuildDir mode programId </> "www" </> "js" </> "runjs.js")
61+
copySource mode programId =
62+
copyFile
63+
(buildRootDir mode </> targetFile programId)
64+
(androidBuildDir mode programId </> "www" </> "js" </> "runjs.js")
65+
66+
setAppName :: BuildMode -> ProgramId -> String -> IO ()
67+
setAppName mode programId appName = do
68+
let configFileName = androidBuildDir mode programId </> "config.xml"
69+
configContents <- ST.readFile configFileName
70+
let tagSoup = parseTags configContents
71+
let newNameTag = [TagOpen "name" [], TagText appName]
72+
writeFile configFileName (renderTags $ newSoup tagSoup newNameTag)
73+
where newSoup soup insertTag = takeWhile nameId soup
74+
++ insertTag
75+
++ drop 2 (dropWhile nameId soup)
76+
nameId = (~/= ("<name>"::String))
5377

5478
buildApk :: BuildMode -> ProgramId -> IO ()
5579
buildApk mode programId = do

codeworld-server/src/Main.hs

+5-1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import qualified Data.ByteString.Lazy as LB
3434
import Data.Char (isSpace)
3535
import Data.List
3636
import Data.Maybe
37+
import qualified Data.Map.Strict as M
3738
import Data.Monoid
3839
import qualified Data.Text as T
3940
import qualified Data.Text.IO as T
@@ -361,6 +362,8 @@ exportAndroidHandler :: Snap()
361362
exportAndroidHandler = do
362363
mode <- getBuildMode
363364
Just source <- getParam "source"
365+
maybeAppName <- getParam "appName"
366+
let appName = BC.unpack $ fromMaybe (BC.pack "CodeWorld App") maybeAppName
364367
let programId = sourceToProgramId source
365368
deployId = sourceToDeployId source
366369
success <- liftIO $ do
@@ -369,7 +372,8 @@ exportAndroidHandler = do
369372
writeDeployLink mode deployId programId
370373
compileIfNeeded mode programId
371374
unless success $ modifyResponse $ setResponseCode 500
372-
liftIO $ buildAndroid mode programId
375+
let appProps = M.fromList [("appName", appName)]
376+
liftIO $ buildAndroid mode programId appProps
373377
let result = CompileResult (unProgramId programId) (unDeployId deployId)
374378
writeLBS (encode result)
375379

codeworld-server/src/Util.hs

+3
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import qualified Data.ByteString.Base64 as B64
3030
import qualified Data.ByteString.Lazy as LB
3131
import Data.Maybe
3232
import Data.Monoid
33+
import qualified Data.Map.Strict as M
3334
import Data.Text (Text)
3435
import qualified Data.Text as T
3536
import qualified Data.Text.Encoding as T
@@ -48,6 +49,8 @@ newtype DeployId = DeployId { unDeployId :: Text } deriving Eq
4849
newtype DirId = DirId { unDirId :: Text} deriving Eq
4950
newtype ShareId = ShareId { unShareId :: Text } deriving Eq
5051

52+
type AppProps = M.Map String String
53+
5154
autocompletePath :: FilePath
5255
autocompletePath = "web/codeworld-base.txt"
5356

web/css/codeworld.css

+9
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,15 @@ body {
157157
cursor: pointer;
158158
}
159159

160+
button {
161+
border: none;
162+
}
163+
164+
button:disabled,
165+
button[disabled] {
166+
background-color: #cccccc !important;
167+
}
168+
160169
.cw-button {
161170
border-radius: 4px;
162171
cursor: pointer;

web/env.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@
9797
<a id="shareButton" class="cw-button yellow" style="display:none" onclick="share()"><i class="mdi mdi-18px mdi-share"></i>&nbsp; Share</a>
9898
<a id="inspectButton" class="cw-button cyan" style="display:none" onclick="inspect()"><i class="mdi mdi-18px mdi-magnify"></i>&nbsp; Inspect</a>
9999
<a class="cw-button red" onclick="stop()"><i class="mdi mdi-18px mdi-stop"></i>&nbsp; Stop</a>
100-
<a class="cw-button green" onclick="exportAndroid()"><i class="mdi mdi-18px mdi-android"></i>&nbsp; Android</a>
100+
<button id="exportAndroidButton" style="display: none" class="cw-button green" onclick="exportAndroid()"><i class="mdi mdi-18px mdi-android"></i>&nbsp; Android</button>
101101
<a class="cw-button green" onclick="compile()"><i class="mdi mdi-18px mdi-play"></i>&nbsp; Run</a>
102102
</div>
103103
</div>

web/js/codeworld.js

+47-5
Original file line numberDiff line numberDiff line change
@@ -690,11 +690,13 @@ function run(hash, dhash, msg, error) {
690690
runner.contentWindow.location.replace(loc);
691691
if (!!navigator.mediaDevices && !!navigator.mediaDevices.getUserMedia) {
692692
document.getElementById('startRecButton').style.display = '';
693+
document.getElementById('exportAndroidButton').style.display = '';
693694
}
694695
} else {
695696
runner.contentWindow.location.replace('about:blank');
696697
document.getElementById('runner').style.display = 'none';
697698
document.getElementById('startRecButton').style.display = 'none';
699+
document.getElementById('exportAndroidButton').style.display = 'none';
698700
}
699701

700702
if (hash || msg) {
@@ -732,14 +734,51 @@ function goto(line, col) {
732734
}
733735

734736
function exportAndroid() {
737+
sweetAlert({
738+
title: "App Information",
739+
text: "App Name",
740+
type: "input",
741+
showCancelButton: true,
742+
confirmButtonText: "Build App",
743+
closeOnConfirm: false,
744+
inputPlaceholder: "CodeWorld App"
745+
},
746+
function(inputValue){
747+
if (inputValue === false) {
748+
return false;
749+
}
750+
if (inputValue === "") {
751+
swal.showInputError("Please enter a name for your app");
752+
return false;
753+
}
754+
compileAndExportAndroid({
755+
appName: inputValue,
756+
});
757+
sweetAlert({
758+
title: "Please Wait",
759+
text: "Your app is being built",
760+
imageUrl: "https://upload.wikimedia.org/wikipedia/commons/b/b1/Loading_icon.gif",
761+
showConfirmButton: false,
762+
allowOutsideClick: false,
763+
allowEscapeKey: false
764+
});
765+
});
766+
}
767+
768+
function compileAndExportAndroid(appProps) {
735769
var src = window.codeworldEditor.getValue();
736770
var data = new FormData();
737771
data.append('source', src);
738772
data.append('mode', window.buildMode);
773+
for(var prop in appProps) {
774+
data.append(prop, appProps[prop]);
775+
}
776+
document.getElementById('exportAndroidButton').disabled = true;
739777

740778
sendHttp('POST', 'exportAndroid', data, function(request) {
741779
if(request.status != 200) {
742-
alert("Android build FAILED");
780+
sweetAlert("Android Build Failed", "Something went wrong!", "error");
781+
document.getElementById('exportAndroidButton').disabled = false;
743782
return;
744783
}
745784
var response = JSON.parse(request.response);
@@ -748,17 +787,19 @@ function exportAndroid() {
748787
var data = new FormData();
749788
data.append('hash', hash);
750789
data.append('mode', window.buildMode);
751-
var props = {};
790+
var props = {};
752791
props.responseType = "blob";
753792

754793
sendHttpWithProps('POST', 'getAndroid', data, props, function(request) {
755794
if(request.status != 200) {
756-
alert("Android fetch FAILED");
795+
sweetAlert("Android Fetch Failed", "Something went wrong!", "error");
796+
document.getElementById('exportAndroidButton').disabled = false;
757797
return;
758798
}
759-
console.log("Success");
799+
swal("App Built!", "Your CodeWorld app will now be downloaded", "success");
800+
760801
var blob = request.response;
761-
var d = new Date();
802+
var d = new Date();
762803
var filename = 'codeworld_app_'
763804
+ d.toDateString().split(' ').join('_') + '_'
764805
+ d.getHours() +':'+ d.getMinutes() +':'+ d.getSeconds()
@@ -774,6 +815,7 @@ function exportAndroid() {
774815
window.URL.revokeObjectURL(url);
775816

776817
a.remove();
818+
document.getElementById('exportAndroidButton').disabled = false;
777819
});
778820

779821
});

0 commit comments

Comments
 (0)