blob: 53d70da769e7ec98fb7da35bf11b54181aa9ee1a [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:convert';
import 'package:lib.app.dart/app.dart';
import 'package:fuchsia.fidl.modular/modular.dart';
import 'package:lib.decomposition.dart/decomposition.dart';
import 'package:fidl/fidl.dart';
import 'package:lib.logging/logging.dart';
import 'package:lib.proposal.dart/proposal.dart';
/// The Concert Agents subscribes to the hotel topic and makes proposals for
/// upcoming concerts (concert list module).
const String _kHotelTopic = 'link/hotel';
/// Global scoping to prevent garbage collection
final ContextReaderProxy _contextReader = new ContextReaderProxy();
ContextListenerImpl _contextListenerImpl;
final ProposalPublisherProxy _proposalPublisher = new ProposalPublisherProxy();
final ApplicationContext _context = new ApplicationContext.fromStartupInfo();
/// Concert ContextListener listens to hotel reservations and makes a concert
/// list proposal
class ContextListenerImpl extends ContextListener {
final ContextListenerBinding _binding = new ContextListenerBinding();
/// Gets the [InterfaceHandle]
///
/// The returned handle should only be used once.
InterfaceHandle<ContextListener> getHandle() => _binding.wrap(this);
@override
Future<Null> onContextUpdate(ContextUpdate result) async {
for (final ContextUpdateEntry entry in result.values) {
if (entry.key != _kHotelTopic || entry.value.isEmpty) {
continue;
}
// TODO(thatguy): There can be more than one value. At some point, use the
// entity type in the ContextQuery instead of using topics as if they are
// types, and handle multiple instances.
dynamic data = json.decode(entry.value[0].content);
if (data != null && data['name'] is String) {
await _createProposal(data['name']);
}
}
}
/// Creates a concert list proposal
Future<Null> _createProposal(String hotelName) async {
String headline = 'Upcoming concerts near $hotelName this week';
final Uri arg = new Uri(
host: 'www.songkick.com',
pathSegments: <String>['metro_areas', '26330-us-sf-bay-area'],
);
Proposal proposal = await createProposal(
id: 'Concerts Near Hotel',
headline: headline,
subheadline: 'powered by Songkick',
color: 0xFF467187,
imageUrl:
'https://images.unsplash.com/photo-1486591978090-58e619d37fe7?dpr=1&auto=format&fit=crop&w=300&h=300&q=80&cs=tinysrgb&crop=&bg=',
actions: <Action>[
new Action.withCreateStory(new CreateStory(
moduleId: 'concert_event_list',
initialData:
json.encode(<String, dynamic>{'view': decomposeUri(arg)})))
],
);
log.fine('proposing concert suggestion');
_proposalPublisher.propose(proposal);
}
}
Future<Null> main(List<dynamic> args) async {
setupLogger();
connectToService(_context.environmentServices, _contextReader.ctrl);
connectToService(_context.environmentServices, _proposalPublisher.ctrl);
ContextQuery query =
const ContextQuery(selector: const <ContextQueryEntry>[const
ContextQueryEntry(
key: _kHotelTopic, value: const ContextSelector(
type: ContextValueType.entity,
meta: const ContextMetadata(
entity: const EntityMetadata(topic: _kHotelTopic)))
)]);
_contextListenerImpl = new ContextListenerImpl();
_contextReader.subscribe(query, _contextListenerImpl.getHandle());
}