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. 23
      mix/qml/CodeEditorView.qml
  5. 15
      mix/qml/ProjectModel.qml
  6. 47
      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) void FileIo::copyFile(QString const& _sourceUrl, QString const& _destUrl)
{ {
if (QUrl(_sourceUrl).scheme() == "qrc")
{
writeFile(_destUrl, readFile(_sourceUrl));
return;
}
QUrl sourceUrl(_sourceUrl); QUrl sourceUrl(_sourceUrl);
QUrl destUrl(_destUrl); QUrl destUrl(_destUrl);
if (!QFile::copy(sourceUrl.path(), destUrl.path())) if (!QFile::copy(sourceUrl.path(), destUrl.path()))

12
mix/HttpServer.cpp

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

4
mix/HttpServer.h

@ -51,11 +51,15 @@ public:
/// Set response for a request /// Set response for a request
/// @param _response Response body. If no response is set, server returns status 200 with empty body /// @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; } 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: private:
QUrl m_url; QUrl m_url;
QString m_content; QString m_content;
QString m_response; QString m_response;
QString m_responseContentType;
friend class HttpServer; friend class HttpServer;
}; };

23
mix/qml/CodeEditorView.qml

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

15
mix/qml/ProjectModel.qml

@ -34,6 +34,7 @@ Item {
property string deploymentAddress: "" property string deploymentAddress: ""
property var listModel: projectListModel property var listModel: projectListModel
property var stateListModel: projectStateListModel.model property var stateListModel: projectStateListModel.model
property CodeEditorView codeEditor: null
//interface //interface
function saveAll() { ProjectModelCode.saveAll(); } function saveAll() { ProjectModelCode.saveAll(); }
@ -53,7 +54,7 @@ Item {
function getDocument(documentId) { return ProjectModelCode.getDocument(documentId); } function getDocument(documentId) { return ProjectModelCode.getDocument(documentId); }
function getDocumentIndex(documentId) { return ProjectModelCode.getDocumentIndex(documentId); } function getDocumentIndex(documentId) { return ProjectModelCode.getDocumentIndex(documentId); }
function addExistingFiles(paths) { ProjectModelCode.doAddExistingFiles(paths); } function addExistingFiles(paths) { ProjectModelCode.doAddExistingFiles(paths); }
function deployProject() { ProjectModelCode.deployProject(); } function deployProject() { ProjectModelCode.deployProject(false); }
Connections { Connections {
target: appContext 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 { ListModel {
id: projectListModel id: projectListModel
} }

47
mix/qml/WebPreview.qml

@ -43,8 +43,8 @@ Item {
} }
function changePage() { function changePage() {
if (pageCombo.currentIndex >=0 && pageCombo.currentIndex < pageListModel.count) { if (pageCombo.currentIndex >= 0 && pageCombo.currentIndex < pageListModel.count) {
setPreviewUrl(pageListModel.get(pageCombo.currentIndex).path); setPreviewUrl(httpServer.url + "/" + pageListModel.get(pageCombo.currentIndex).documentId);
} else { } else {
setPreviewUrl(""); setPreviewUrl("");
} }
@ -54,7 +54,7 @@ Item {
onAppLoaded: { onAppLoaded: {
//We need to load the container using file scheme so that web security would allow loading local files in iframe //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"); var containerPage = fileIo.readFile("qrc:///qml/html/WebContainer.html");
webView.loadHtml(containerPage, "file:///WebContainer.html") webView.loadHtml(containerPage, httpServer.url + "/WebContainer.html")
} }
} }
@ -112,16 +112,35 @@ Item {
accept: true accept: true
port: 8893 port: 8893
onClientConnected: { onClientConnected: {
//filter polling spam var urlPath = _request.url.toString();
//TODO: do it properly if (urlPath.indexOf("/rpc/") === 0)
//var log = _request.content.indexOf("eth_changed") < 0; {
var log = true; //jsonrpc request
if (log) //filter polling requests //TODO: do it properly
console.log(_request.content); var log = _request.content.indexOf("eth_changed") < 0;
var response = clientModel.apiCall(_request.content); if (log)
if (log) console.log(_request.content);
console.log(response); var response = clientModel.apiCall(_request.content);
_request.setResponse(response); if (log)
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);
}
} }
} }
@ -163,7 +182,7 @@ Item {
onLoadingChanged: { onLoadingChanged: {
if (!loading) { if (!loading) {
initialized = true; initialized = true;
webView.runJavaScript("init(\"" + httpServer.url + "\")"); webView.runJavaScript("init(\"" + httpServer.url + "/rpc/\")");
if (pendingPageUrl) if (pendingPageUrl)
setPreviewUrl(pendingPageUrl); setPreviewUrl(pendingPageUrl);
} }

7
mix/qml/html/WebContainer.html

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

17
mix/qml/js/ProjectModel.js

@ -253,11 +253,17 @@ function generateFileName(name, extension) {
var jsonRpcRequestId = 1; var jsonRpcRequestId = 1;
function deployProject() { function deployProject(force) {
saveAll(); //TODO: ask user 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"; var jsonRpcUrl = "http://localhost:8080";
console.log("Deploying " + deploymentId + " to " + jsonRpcUrl); console.log("Deploying " + deploymentId + " to " + jsonRpcUrl);
deploymentStarted(); deploymentStarted();
@ -283,7 +289,7 @@ function deployProject() {
console.log("Created contract, address: " + address); console.log("Created contract, address: " + address);
finalizeDeployment(deploymentId, address); finalizeDeployment(deploymentId, address);
} else { } 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); console.log(errorText);
deploymentError(errorText); deploymentError(errorText);
} }
@ -324,16 +330,17 @@ function finalizeDeployment(deploymentId, address) {
"// Autogenerated by Mix\n" + "// Autogenerated by Mix\n" +
"var web3 = require(\"web3\");\n" + "var web3 = require(\"web3\");\n" +
"var contractInterface = " + codeModel.code.contractInterface + ";\n" + "var contractInterface = " + codeModel.code.contractInterface + ";\n" +
"deployment = {\n" + "deploy = {\n" +
"\tweb3: web3,\n" + "\tweb3: web3,\n" +
"\tcontractAddress: \"" + address + "\",\n" + "\tcontractAddress: \"" + address + "\",\n" +
"\tcontractInterface: contractInterface,\n" + "\tcontractInterface: contractInterface,\n" +
"};\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); fileIo.writeFile(deploymentDir + "deployment.js", deploymentJs);
//copy scripts //copy scripts
fileIo.copyFile("qrc:///js/bignumber.min.js", deploymentDir + "bignumber.min.js"); fileIo.copyFile("qrc:///js/bignumber.min.js", deploymentDir + "bignumber.min.js");
fileIo.copyFile("qrc:///js/webthree.js", deploymentDir + "ethereum.js"); fileIo.copyFile("qrc:///js/webthree.js", deploymentDir + "ethereum.js");
deploymentAddress = address;
saveProject(); saveProject();
deploymentComplete(); deploymentComplete();
} }

Loading…
Cancel
Save