Browse Source

web preview via http server

cl-refactor
arkpar 10 years ago
parent
commit
d2b72415e8
  1. 6
      mix/FileIo.cpp
  2. 12
      mix/HttpServer.cpp
  3. 4
      mix/HttpServer.h
  4. 19
      mix/qml/CodeEditorView.qml
  5. 15
      mix/qml/ProjectModel.qml
  6. 33
      mix/qml/WebPreview.qml
  7. 7
      mix/qml/html/WebContainer.html
  8. 17
      mix/qml/js/ProjectModel.js

6
mix/FileIo.cpp

@ -70,6 +70,12 @@ void FileIo::writeFile(QString const& _url, QString const& _data)
void FileIo::copyFile(QString const& _sourceUrl, QString const& _destUrl)
{
if (QUrl(_sourceUrl).scheme() == "qrc")
{
writeFile(_destUrl, readFile(_sourceUrl));
return;
}
QUrl sourceUrl(_sourceUrl);
QUrl destUrl(_destUrl);
if (!QFile::copy(sourceUrl.path(), destUrl.path()))

12
mix/HttpServer.cpp

@ -132,23 +132,25 @@ void HttpServer::readClient()
if (socket->canReadLine())
{
QString hdr = QString(socket->readLine());
if (hdr.startsWith("POST"))
if (hdr.startsWith("POST") || hdr.startsWith("GET"))
{
QUrl url(hdr.split(' ')[1]);
QString l;
do
l = socket->readLine();
while (!(l.isEmpty() || l == "\r" || l == "\r\n"));
QString content = socket->readAll();
QUrl url;
std::unique_ptr<HttpRequest> request(new HttpRequest(this, url, content));
clientConnected(request.get());
QTextStream os(socket);
os.setAutoDetectUnicode(true);
QString q;
///@todo: allow setting response content-type, charset, etc
os << "HTTP/1.0 200 Ok\r\n"
"Content-Type: text/plain; charset=\"utf-8\"\r\n"
"\r\n";
os << "HTTP/1.0 200 Ok\r\n";
if (!request->m_responseContentType.isEmpty())
os << "Content-Type: " << request->m_responseContentType << "; ";
os << "charset=\"utf-8\"\r\n\r\n";
os << request->m_response;
}
}

4
mix/HttpServer.h

@ -51,11 +51,15 @@ public:
/// Set response for a request
/// @param _response Response body. If no response is set, server returns status 200 with empty body
Q_INVOKABLE void setResponse(QString const& _response) { m_response = _response; }
/// Set response content type
/// @param _contentType Response content type string. text/plain by default
Q_INVOKABLE void setResponseContentType(QString const& _contentType) { m_responseContentType = _contentType ; }
private:
QUrl m_url;
QString m_content;
QString m_response;
QString m_responseContentType;
friend class HttpServer;
};

19
mix/qml/CodeEditorView.qml

@ -4,18 +4,26 @@ import QtQuick.Layouts 1.0
import QtQuick.Controls 1.0
Item {
id: codeEditorView
property string currentDocumentId: ""
signal documentEdit(string documentId)
function getDocumentText(documentId) {
for (i = 0; i < editorListModel.count; i++) {
for (var i = 0; i < editorListModel.count; i++) {
if (editorListModel.get(i).documentId === documentId) {
return editors.itemAt(i).getText();
return editors.itemAt(i).item.getText();
}
}
return "";
}
function isDocumentOpen(documentId) {
for (var i = 0; i < editorListModel.count; i++)
if (editorListModel.get(i).documentId === documentId)
return true;
return false;
}
function openDocument(document) {
loadDocument(document);
currentDocumentId = document.documentId;
@ -31,13 +39,16 @@ Item {
function doLoadDocument(editor, document) {
var data = fileIo.readFile(document.path);
if (document.isContract)
editor.onEditorTextChanged.connect(function() {
documentEdit(document.documentId);
if (document.isContract)
codeModel.registerCodeChange(editor.getText());
});
editor.setText(data, document.syntaxMode);
}
Component.onCompleted: projectModel.codeEditor = codeEditorView;
Connections {
target: projectModel
onDocumentOpened: {

15
mix/qml/ProjectModel.qml

@ -34,6 +34,7 @@ Item {
property string deploymentAddress: ""
property var listModel: projectListModel
property var stateListModel: projectStateListModel.model
property CodeEditorView codeEditor: null
//interface
function saveAll() { ProjectModelCode.saveAll(); }
@ -53,7 +54,7 @@ Item {
function getDocument(documentId) { return ProjectModelCode.getDocument(documentId); }
function getDocumentIndex(documentId) { return ProjectModelCode.getDocumentIndex(documentId); }
function addExistingFiles(paths) { ProjectModelCode.doAddExistingFiles(paths); }
function deployProject() { ProjectModelCode.deployProject(); }
function deployProject() { ProjectModelCode.deployProject(false); }
Connections {
target: appContext
@ -88,6 +89,18 @@ Item {
}
}
MessageDialog {
id: deployWarningDialog
title: qsTr("Project")
text: qsTr("This project has been already deployed to the network. Do you want to re-deploy it?")
standardButtons: StandardButton.Ok | StandardButton.Cancel
icon: StandardIcon.Question
onAccepted: {
ProjectModelCode.deployProject(true);
}
}
ListModel {
id: projectListModel
}

33
mix/qml/WebPreview.qml

@ -44,7 +44,7 @@ Item {
function changePage() {
if (pageCombo.currentIndex >= 0 && pageCombo.currentIndex < pageListModel.count) {
setPreviewUrl(pageListModel.get(pageCombo.currentIndex).path);
setPreviewUrl(httpServer.url + "/" + pageListModel.get(pageCombo.currentIndex).documentId);
} else {
setPreviewUrl("");
}
@ -54,7 +54,7 @@ Item {
onAppLoaded: {
//We need to load the container using file scheme so that web security would allow loading local files in iframe
var containerPage = fileIo.readFile("qrc:///qml/html/WebContainer.html");
webView.loadHtml(containerPage, "file:///WebContainer.html")
webView.loadHtml(containerPage, httpServer.url + "/WebContainer.html")
}
}
@ -112,10 +112,12 @@ Item {
accept: true
port: 8893
onClientConnected: {
//filter polling spam
//TODO: do it properly
//var log = _request.content.indexOf("eth_changed") < 0;
var log = true;
var urlPath = _request.url.toString();
if (urlPath.indexOf("/rpc/") === 0)
{
//jsonrpc request
//filter polling requests //TODO: do it properly
var log = _request.content.indexOf("eth_changed") < 0;
if (log)
console.log(_request.content);
var response = clientModel.apiCall(_request.content);
@ -123,6 +125,23 @@ Item {
console.log(response);
_request.setResponse(response);
}
else
{
//document request
var documentId = urlPath.substr(urlPath.lastIndexOf("/") + 1);
var content = "";
if (projectModel.codeEditor.isDocumentOpen(documentId))
content = projectModel.codeEditor.getDocumentText(documentId);
else
content = fileIo.readFile(projectModel.getDocument(documentId).path);
if (documentId === pageListModel.get(pageCombo.currentIndex).documentId) {
//root page, inject deployment script
content = "<script>deploy=parent.deploy</script>\n" + content;
_request.setResponseContentType("text/html");
}
_request.setResponse(content);
}
}
}
ColumnLayout {
@ -163,7 +182,7 @@ Item {
onLoadingChanged: {
if (!loading) {
initialized = true;
webView.runJavaScript("init(\"" + httpServer.url + "\")");
webView.runJavaScript("init(\"" + httpServer.url + "/rpc/\")");
if (pendingPageUrl)
setPreviewUrl(pendingPageUrl);
}

7
mix/qml/html/WebContainer.html

@ -21,13 +21,18 @@ updateContract = function(address, contractFace) {
window.contractAddress = address;
window.contractInterface = contractFace;
window.contract = window.web3.eth.contract(address, contractFace);
window.deploy = {
contractAddress: address,
contractInterface: contractFace,
contract: window.contract
};
}
};
init = function(url) {
web3 = require('web3');
web3.setProvider(new web3.providers.HttpSyncProvider(url));
window.web3 = web3;
web3.setProvider(new web3.providers.HttpSyncProvider(url));
};
</script>

17
mix/qml/js/ProjectModel.js

@ -253,11 +253,17 @@ function generateFileName(name, extension) {
var jsonRpcRequestId = 1;
function deployProject() {
function deployProject(force) {
saveAll(); //TODO: ask user
var deploymentId = Date.now().toLocaleString("ddMMyyyHHmmsszzz");
if (!force && deploymentAddress !== "") {
deployWarningDialog.visible = true;
return;
}
var date = new Date();
var deploymentId = date.toLocaleString(Qt.locale(), "ddMMyyHHmmsszzz");
var jsonRpcUrl = "http://localhost:8080";
console.log("Deploying " + deploymentId + " to " + jsonRpcUrl);
deploymentStarted();
@ -283,7 +289,7 @@ function deployProject() {
console.log("Created contract, address: " + address);
finalizeDeployment(deploymentId, address);
} else {
var errorText = qsTr("Deployment error: RPC server HTTP status ") + http.status;
var errorText = qsTr("Deployment error: RPC server HTTP status ") + httpRequest.status;
console.log(errorText);
deploymentError(errorText);
}
@ -324,16 +330,17 @@ function finalizeDeployment(deploymentId, address) {
"// Autogenerated by Mix\n" +
"var web3 = require(\"web3\");\n" +
"var contractInterface = " + codeModel.code.contractInterface + ";\n" +
"deployment = {\n" +
"deploy = {\n" +
"\tweb3: web3,\n" +
"\tcontractAddress: \"" + address + "\",\n" +
"\tcontractInterface: contractInterface,\n" +
"};\n" +
"deplyment.contract = web3.eth.contract(deplyment.contractAddress, deployment.contractInterface);\n";
"deploy.contract = web3.eth.contract(deploy.contractAddress, deploy.contractInterface);\n";
fileIo.writeFile(deploymentDir + "deployment.js", deploymentJs);
//copy scripts
fileIo.copyFile("qrc:///js/bignumber.min.js", deploymentDir + "bignumber.min.js");
fileIo.copyFile("qrc:///js/webthree.js", deploymentDir + "ethereum.js");
deploymentAddress = address;
saveProject();
deploymentComplete();
}

Loading…
Cancel
Save