Download squeaks with custom params (#1108)

* Add rpc method for subscribe squeak entry

* Download squeak without reloading page using squeak entry subscription

* Fix stream termination of component unmount for subscribe squeak entry

* Update frontend build
This commit is contained in:
Jonathan Zernik 2021-08-29 22:39:10 -07:00 committed by GitHub
parent c38b177340
commit a0d78844da
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 147 additions and 41 deletions

View file

@ -86,7 +86,9 @@ export default function SqueakDetailItem({
event.preventDefault();
console.log('Handling download click...');
downloadSqueakRequest(hash, (response) => {
reloadSqueak();
// TODO: Nothing for now. This will eventually show something
// when downloadSqueakRequest becomes a streaming method.
// reloadSqueak();
});
};

View file

@ -23,6 +23,7 @@ import SqueakReplies from '../../components/SqueakReplies';
import {
getSqueakDisplayRequest,
subscribeSqueakDisplayRequest,
getAncestorSqueakDisplaysRequest,
getReplySqueakDisplaysRequest,
getNetworkRequest,
@ -44,6 +45,9 @@ export default function SqueakPage() {
const getSqueak = (hash) => {
getSqueakDisplayRequest(hash, setSqueak);
};
const subscribeSqueak = (hash) => {
return subscribeSqueakDisplayRequest(hash, setSqueak);
};
const getAncestorSqueaks = (hash) => {
getAncestorSqueakDisplaysRequest(hash, setAncestorSqueaks);
};
@ -69,6 +73,10 @@ export default function SqueakPage() {
useEffect(() => {
getSqueak(hash);
}, [hash]);
useEffect(() => {
const stream = subscribeSqueak(hash);
return () => stream.cancel();
}, [hash]);
useEffect(() => {
getAncestorSqueaks(hash);
}, [hash]);

View file

@ -29,6 +29,7 @@ import {
GetContactProfilesRequest,
MakeSqueakRequest,
GetSqueakDisplayRequest,
SubscribeSqueakDisplayRequest,
GetAncestorSqueakDisplaysRequest,
GetReplySqueakDisplaysRequest,
GetSqueakProfileByAddressRequest,
@ -625,3 +626,20 @@ export function subscribeBuyOffersRequest(hash, handleResponse) {
alert(`Stream ended: ${end}`);
});
}
export function subscribeSqueakDisplayRequest(hash, handleResponse) {
const request = new SubscribeSqueakDisplayRequest();
request.setSqueakHash(hash);
const stream = client.subscribeSqueakDisplay(request);
stream.on('data', (response) => {
handleResponse(response.getSqueakDisplayEntry());
});
stream.on('end', (end) => {
// stream end signal
console.log(end);
alert(`Stream ended: ${end}`);
});
console.log("Stream object:");
console.log(stream);
return stream;
}

View file

@ -49,6 +49,7 @@ from tests.util import make_squeak
from tests.util import open_channel
from tests.util import open_peer_connection
from tests.util import subscribe_connected_peers
from tests.util import subscribe_squeak_entry
def test_get_network(admin_stub):
@ -684,41 +685,49 @@ def test_download_single_squeak(
signing_profile_id,
saved_squeak_hash,
):
# Get the squeak display item (should be empty)
squeak_display_entry = get_squeak_display(
other_admin_stub, saved_squeak_hash)
assert squeak_display_entry is None
# Get buy offers for the squeak hash (should be empty)
get_buy_offers_response = other_admin_stub.GetBuyOffers(
squeak_admin_pb2.GetBuyOffersRequest(
squeak_hash=saved_squeak_hash,
with subscribe_squeak_entry(other_admin_stub, saved_squeak_hash) as subscription_queue:
# Get the squeak display item (should be empty)
squeak_display_entry = get_squeak_display(
other_admin_stub, saved_squeak_hash)
assert squeak_display_entry is None
# Get buy offers for the squeak hash (should be empty)
get_buy_offers_response = other_admin_stub.GetBuyOffers(
squeak_admin_pb2.GetBuyOffersRequest(
squeak_hash=saved_squeak_hash,
)
)
)
# print(get_buy_offers_response)
assert len(get_buy_offers_response.offers) == 0
# print(get_buy_offers_response)
assert len(get_buy_offers_response.offers) == 0
# Download squeak
download_squeak(other_admin_stub, saved_squeak_hash)
time.sleep(5)
# Download squeak
download_squeak(other_admin_stub, saved_squeak_hash)
time.sleep(5)
# Get the squeak display item
squeak_display_entry = get_squeak_display(
other_admin_stub, saved_squeak_hash)
assert squeak_display_entry is not None
# Get the squeak display item
squeak_display_entry = get_squeak_display(
other_admin_stub, saved_squeak_hash)
assert squeak_display_entry is not None
# Download offer
download_offers(other_admin_stub, saved_squeak_hash)
time.sleep(5)
# Download offer
download_offers(other_admin_stub, saved_squeak_hash)
time.sleep(5)
# Get the buy offer
get_buy_offers_response = other_admin_stub.GetBuyOffers(
squeak_admin_pb2.GetBuyOffersRequest(
squeak_hash=saved_squeak_hash,
# Get the buy offer
get_buy_offers_response = other_admin_stub.GetBuyOffers(
squeak_admin_pb2.GetBuyOffersRequest(
squeak_hash=saved_squeak_hash,
)
)
)
# print(get_buy_offers_response)
assert len(get_buy_offers_response.offers) > 0
# print(get_buy_offers_response)
assert len(get_buy_offers_response.offers) > 0
item = subscription_queue.get()
print("item:")
print(item)
assert item.squeak_hash == saved_squeak_hash
def test_get_squeak_details(admin_stub, saved_squeak_hash):

View file

@ -326,3 +326,23 @@ def delete_profile(node_stub, profile_id):
profile_id=profile_id,
)
)
@contextmanager
def subscribe_squeak_entry(node_stub, squeak_hash):
q = queue.Queue()
subscribe_squeak_entry_response = node_stub.SubscribeSqueakDisplay(
squeak_admin_pb2.SubscribeSqueakDisplayRequest(
squeak_hash=squeak_hash,
)
)
def enqueue_results():
for result in subscribe_squeak_entry_response:
q.put(result.squeak_display_entry)
threading.Thread(
target=enqueue_results,
).start()
yield q
subscribe_squeak_entry_response.cancel()

View file

@ -280,6 +280,10 @@ service SqueakAdmin {
*/
rpc SubscribeBuyOffers (SubscribeBuyOffersRequest) returns (stream GetBuyOfferReply) {}
/** sqkadmin: `subscribesqueakdisplay`
*/
rpc SubscribeSqueakDisplay (SubscribeSqueakDisplayRequest) returns (stream GetSqueakDisplayReply) {}
}
message CreateSigningProfileRequest {
@ -1002,3 +1006,8 @@ message SubscribeBuyOffersRequest {
/// Hash of the squeak.
string squeak_hash = 1;
}
message SubscribeSqueakDisplayRequest {
/// Hash of the squeak.
string squeak_hash = 1;
}

View file

@ -797,3 +797,24 @@ class SqueakAdminServerHandler(object):
yield squeak_admin_pb2.GetBuyOfferReply(
offer=offer_msg,
)
def handle_subscribe_squeak_display(self, request, stopped):
squeak_hash_str = request.squeak_hash
squeak_hash = bytes.fromhex(squeak_hash_str)
logger.info(
"Handle subscribe squeak display for hash: {}".format(squeak_hash_str))
squeak_display_stream = self.squeak_controller.subscribe_squeak_entry(
squeak_hash,
stopped,
)
for squeak_display in squeak_display_stream:
if squeak_display is None:
yield squeak_admin_pb2.GetSqueakDisplayReply(
squeak_display_entry=None
)
else:
display_message = squeak_entry_to_message(
squeak_display)
yield squeak_admin_pb2.GetSqueakDisplayReply(
squeak_display_entry=display_message
)

View file

@ -300,3 +300,15 @@ class SqueakAdminServerServicer(squeak_admin_pb2_grpc.SqueakAdminServicer):
request,
stopped,
)
def SubscribeSqueakDisplay(self, request, context):
stopped = threading.Event()
def on_rpc_done():
logger.info("Stopping SubscribeSqueakDisplay.")
stopped.set()
context.add_callback(on_rpc_done)
return self.handler.handle_subscribe_squeak_display(
request,
stopped,
)

View file

@ -1,14 +1,14 @@
{
"files": {
"main.js": "/static/js/main.c99c3d4f.chunk.js",
"main.js.map": "/static/js/main.c99c3d4f.chunk.js.map",
"main.js": "/static/js/main.a67fc880.chunk.js",
"main.js.map": "/static/js/main.a67fc880.chunk.js.map",
"runtime-main.js": "/static/js/runtime-main.9f0ba400.js",
"runtime-main.js.map": "/static/js/runtime-main.9f0ba400.js.map",
"static/css/2.ea4ba2f0.chunk.css": "/static/css/2.ea4ba2f0.chunk.css",
"static/js/2.ae9e5f97.chunk.js": "/static/js/2.ae9e5f97.chunk.js",
"static/js/2.ae9e5f97.chunk.js.map": "/static/js/2.ae9e5f97.chunk.js.map",
"index.html": "/index.html",
"precache-manifest.8de724ab18db303eb642bebb45add2c3.js": "/precache-manifest.8de724ab18db303eb642bebb45add2c3.js",
"precache-manifest.d67005e2e73ac40adcbc8e355616292e.js": "/precache-manifest.d67005e2e73ac40adcbc8e355616292e.js",
"service-worker.js": "/service-worker.js",
"static/css/2.ea4ba2f0.chunk.css.map": "/static/css/2.ea4ba2f0.chunk.css.map",
"static/js/2.ae9e5f97.chunk.js.LICENSE.txt": "/static/js/2.ae9e5f97.chunk.js.LICENSE.txt",
@ -20,6 +20,6 @@
"static/js/runtime-main.9f0ba400.js",
"static/css/2.ea4ba2f0.chunk.css",
"static/js/2.ae9e5f97.chunk.js",
"static/js/main.c99c3d4f.chunk.js"
"static/js/main.a67fc880.chunk.js"
]
}

View file

@ -1 +1 @@
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="shortcut icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"/><meta name="theme-color" content="#000000"/><link rel="manifest" href="/manifest.json"/><title>Squeak Node</title><meta name="description" content="Squeak Node is a frontend for accessing a squeak node"><meta name="keywords" content="squeak, bitcoin, lightning"><meta name="author" content="Flatlogic LLC."><link href="/static/css/2.ea4ba2f0.chunk.css" rel="stylesheet"></head><body style="font-family:Roboto,sans-serif"><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,f,l=r[0],a=r[1],i=r[2],c=0,s=[];c<l.length;c++)f=l[c],Object.prototype.hasOwnProperty.call(o,f)&&o[f]&&s.push(o[f][0]),o[f]=0;for(n in a)Object.prototype.hasOwnProperty.call(a,n)&&(e[n]=a[n]);for(p&&p(r);s.length;)s.shift()();return u.push.apply(u,i||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,l=1;l<t.length;l++){var a=t[l];0!==o[a]&&(n=!1)}n&&(u.splice(r--,1),e=f(f.s=t[0]))}return e}var n={},o={1:0},u=[];function f(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,f),t.l=!0,t.exports}f.m=e,f.c=n,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(e,r){if(1&r&&(e=f(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)f.d(t,n,function(r){return e[r]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var l=this["webpackJsonpsqueak-node-frontend"]=this["webpackJsonpsqueak-node-frontend"]||[],a=l.push.bind(l);l.push=r,l=l.slice();for(var i=0;i<l.length;i++)r(l[i]);var p=a;t()}([])</script><script src="/static/js/2.ae9e5f97.chunk.js"></script><script src="/static/js/main.c99c3d4f.chunk.js"></script></body></html>
<!doctype html><html lang="en"><head><meta charset="utf-8"/><link rel="shortcut icon" href="/favicon.ico"/><meta name="viewport" content="width=device-width,initial-scale=1,shrink-to-fit=no"/><meta name="theme-color" content="#000000"/><link rel="manifest" href="/manifest.json"/><title>Squeak Node</title><meta name="description" content="Squeak Node is a frontend for accessing a squeak node"><meta name="keywords" content="squeak, bitcoin, lightning"><meta name="author" content="Flatlogic LLC."><link href="/static/css/2.ea4ba2f0.chunk.css" rel="stylesheet"></head><body style="font-family:Roboto,sans-serif"><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div><script>!function(e){function r(r){for(var n,f,l=r[0],a=r[1],i=r[2],c=0,s=[];c<l.length;c++)f=l[c],Object.prototype.hasOwnProperty.call(o,f)&&o[f]&&s.push(o[f][0]),o[f]=0;for(n in a)Object.prototype.hasOwnProperty.call(a,n)&&(e[n]=a[n]);for(p&&p(r);s.length;)s.shift()();return u.push.apply(u,i||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,l=1;l<t.length;l++){var a=t[l];0!==o[a]&&(n=!1)}n&&(u.splice(r--,1),e=f(f.s=t[0]))}return e}var n={},o={1:0},u=[];function f(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,f),t.l=!0,t.exports}f.m=e,f.c=n,f.d=function(e,r,t){f.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},f.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},f.t=function(e,r){if(1&r&&(e=f(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(f.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)f.d(t,n,function(r){return e[r]}.bind(null,n));return t},f.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return f.d(r,"a",r),r},f.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},f.p="/";var l=this["webpackJsonpsqueak-node-frontend"]=this["webpackJsonpsqueak-node-frontend"]||[],a=l.push.bind(l);l.push=r,l=l.slice();for(var i=0;i<l.length;i++)r(l[i]);var p=a;t()}([])</script><script src="/static/js/2.ae9e5f97.chunk.js"></script><script src="/static/js/main.a67fc880.chunk.js"></script></body></html>

View file

@ -1,6 +1,6 @@
self.__precacheManifest = (self.__precacheManifest || []).concat([
{
"revision": "f04c018404e00f36d08b7a800568d90c",
"revision": "5fc503e15ce3e6c9a33a90edac407ae8",
"url": "/index.html"
},
{
@ -16,8 +16,8 @@ self.__precacheManifest = (self.__precacheManifest || []).concat([
"url": "/static/js/2.ae9e5f97.chunk.js.LICENSE.txt"
},
{
"revision": "2e3182f3f6d47fff0214",
"url": "/static/js/main.c99c3d4f.chunk.js"
"revision": "fee5a9e403984952b91e",
"url": "/static/js/main.a67fc880.chunk.js"
},
{
"revision": "cc9816de5a8639d377ea",

View file

@ -14,7 +14,7 @@
importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
importScripts(
"/precache-manifest.8de724ab18db303eb642bebb45add2c3.js"
"/precache-manifest.d67005e2e73ac40adcbc8e355616292e.js"
);
self.addEventListener('message', (event) => {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -48,6 +48,7 @@ from squeaknode.core.sent_payment_summary import SentPaymentSummary
from squeaknode.core.squeak_entry import SqueakEntry
from squeaknode.core.squeak_peer import SqueakPeer
from squeaknode.core.squeak_profile import SqueakProfile
from squeaknode.core.util import get_hash
from squeaknode.core.util import is_address_valid
from squeaknode.core.util import squeak_matches_interest
from squeaknode.network.peer import Peer
@ -594,6 +595,7 @@ class SqueakController:
)
def download_squeaks(self):
# TODO: Don't use get interested locator, instead use params from request.
locator = self.get_interested_locator()
getsqueaks_msg = msg_getsqueaks(
locator=locator,
@ -671,3 +673,8 @@ class SqueakController:
for received_offer in self.new_received_offer_listener.yield_items(stopped):
if received_offer.squeak_hash == squeak_hash:
yield received_offer
def subscribe_squeak_entry(self, squeak_hash: bytes, stopped: threading.Event):
for item in self.new_squeak_listener.yield_items(stopped):
if squeak_hash == get_hash(item):
yield self.get_squeak_entry(squeak_hash)