diff --git a/appinfo/routes.php b/appinfo/routes.php index ee82c4e..b4f73fc 100644 --- a/appinfo/routes.php +++ b/appinfo/routes.php @@ -24,7 +24,7 @@ return [ 'resources' => [ 'favorite' => ['url' => '/api/favorites'], - 'recent' => ['url' => '/api/recents'], + 'recent' => ['url' => '/api/recent'], ], 'routes' => [ @@ -99,6 +99,12 @@ return [ 'url' => '/api/0.1/{path}', 'verb' => 'OPTIONS', 'requirements' => ['path' => '.+'] + ], + [ + 'name' => 'recent_api#preflighted_cors', + 'url' => '/api/0.1/{path}', + 'verb' => 'OPTIONS', + 'requirements' => ['path' => '.+'] ] ] diff --git a/css/dashboard.css b/css/dashboard.css new file mode 100644 index 0000000..4e0568b --- /dev/null +++ b/css/dashboard.css @@ -0,0 +1,7 @@ +.icon-mail { + background-image: url(./../img/radio-trans.svg); +} + +body.theme--dark .icon-mail { + background-image: url(./../img/radio.svg); +} diff --git a/lib/Controller/FavoriteController.php b/lib/Controller/FavoriteController.php index c6221de..722d333 100644 --- a/lib/Controller/FavoriteController.php +++ b/lib/Controller/FavoriteController.php @@ -3,13 +3,13 @@ namespace OCA\Radio\Controller; use OCA\Radio\AppInfo\Application; -use OCA\Radio\Service\StationService; +use OCA\Radio\Service\FavoriteService; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\DataResponse; use OCP\IRequest; class FavoriteController extends Controller { - /** @var StationService */ + /** @var FavoriteService */ private $service; /** @var string */ @@ -18,7 +18,7 @@ class FavoriteController extends Controller { use Errors; public function __construct(IRequest $request, - StationService $service, + FavoriteService $service, $userId) { parent::__construct(Application::APP_ID, $request); $this->service = $service; diff --git a/lib/Controller/RecentController.php b/lib/Controller/RecentController.php new file mode 100644 index 0000000..8f39145 --- /dev/null +++ b/lib/Controller/RecentController.php @@ -0,0 +1,70 @@ +service = $service; + $this->userId = $userId; + } + + /** + * @NoAdminRequired + */ + public function index(): DataResponse { + return new DataResponse($this->service->findAll($this->userId)); + } + + /** + * @NoAdminRequired + */ + public function show(int $id): DataResponse { + return $this->handleNotFound(function () use ($id) { + return $this->service->find($id, $this->userId); + }); + } + + /** + * @NoAdminRequired + */ + public function create(string $stationuuid, string $name, string $favicon, string $urlresolved): DataResponse { + return new DataResponse($this->service->create($stationuuid, $name, + $favicon, $urlresolved, $this->userId)); + } + + /** + * @NoAdminRequired + */ + public function update(int $id, string $stationuuid, + string $name, string $favicon, string $urlresolved): DataResponse { + return $this->handleNotFound(function () use ($id, $stationuuid, $name, $favicon, $urlresolved) { + return $this->service->update($id, $stationuuid, $name, $favicon, $urlresolved, $this->userId); + }); + } + + /** + * @NoAdminRequired + */ + public function destroy(int $id): DataResponse { + return $this->handleNotFound(function () use ($id) { + return $this->service->delete($id, $this->userId); + }); + } +} diff --git a/lib/Db/StationMapper.php b/lib/Db/FavoriteMapper.php similarity index 96% rename from lib/Db/StationMapper.php rename to lib/Db/FavoriteMapper.php index 74aa992..c83ea3d 100644 --- a/lib/Db/StationMapper.php +++ b/lib/Db/FavoriteMapper.php @@ -8,7 +8,7 @@ use OCP\AppFramework\Db\QBMapper; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; -class StationMapper extends QBMapper { +class FavoriteMapper extends QBMapper { public function __construct(IDBConnection $db) { parent::__construct($db, 'favorites', Station::class); } diff --git a/lib/Db/RecentMapper.php b/lib/Db/RecentMapper.php new file mode 100644 index 0000000..10b640f --- /dev/null +++ b/lib/Db/RecentMapper.php @@ -0,0 +1,45 @@ +db->getQueryBuilder(); + $qb->select('*') + ->from('recent') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id, IQueryBuilder::PARAM_INT))) + ->andWhere($qb->expr()->eq('user_id', $qb->createNamedParameter($userId))); + return $this->findEntity($qb); + } + + /** + * @param string $userId + * @return array + */ + public function findAll(string $userId): array { + /* @var $qb IQueryBuilder */ + $qb = $this->db->getQueryBuilder(); + $qb->select('*') + ->from('recent') + ->where($qb->expr()->eq('user_id', $qb->createNamedParameter($userId))); + return $this->findEntities($qb); + } +} diff --git a/lib/Service/FavoriteService.php b/lib/Service/FavoriteService.php new file mode 100644 index 0000000..b9d7aea --- /dev/null +++ b/lib/Service/FavoriteService.php @@ -0,0 +1,80 @@ +mapper = $mapper; + } + + public function findAll(string $userId): array { + return $this->mapper->findAll($userId); + } + + private function handleException(Exception $e): void { + if ($e instanceof DoesNotExistException || + $e instanceof MultipleObjectsReturnedException) { + throw new StationNotFound($e->getMessage()); + } else { + throw $e; + } + } + + public function find($id, $userId) { + try { + return $this->mapper->find($id, $userId); + + // in order to be able to plug in different storage backends like files + // for instance it is a good idea to turn storage related exceptions + // into service related exceptions so controllers and service users + // have to deal with only one type of exception + } catch (Exception $e) { + $this->handleException($e); + } + } + + public function create($stationuuid, $name, $favicon, $urlresolved, $userId) { + $station = new Station(); + $station->setStationuuid($stationuuid); + $station->setName($name); + $station->setFavicon($favicon); + $station->setUrlresolved($urlresolved); + $station->setUserId($userId); + return $this->mapper->insert($station); + } + + public function update($id, $stationuuid, $name, $favicon, $urlresolved, $userId) { + try { + $station = $this->mapper->find($id, $userId); + $station->setStationuuid($stationuuid); + $station->setName($name); + $station->setFavicon($favicon); + $station->setUrlresolved($urlresolved); + return $this->mapper->update($station); + } catch (Exception $e) { + $this->handleException($e); + } + } + + public function delete($id, $userId) { + try { + $station = $this->mapper->find($id, $userId); + $this->mapper->delete($station); + return $station; + } catch (Exception $e) { + $this->handleException($e); + } + } +} diff --git a/lib/Service/StationService.php b/lib/Service/RecentService.php similarity index 93% rename from lib/Service/StationService.php rename to lib/Service/RecentService.php index 15b1947..4b67f8e 100644 --- a/lib/Service/StationService.php +++ b/lib/Service/RecentService.php @@ -8,14 +8,14 @@ use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCA\Radio\Db\Station; -use OCA\Radio\Db\StationMapper; +use OCA\Radio\Db\RecentMapper; -class StationService { +class RecentService { - /** @var StationMapper */ + /** @var RecentMapper */ private $mapper; - public function __construct(StationMapper $mapper) { + public function __construct(RecentMapper $mapper) { $this->mapper = $mapper; } diff --git a/src/components/Main.vue b/src/components/Main.vue index 6fd8d7e..1b1ac16 100644 --- a/src/components/Main.vue +++ b/src/components/Main.vue @@ -157,7 +157,7 @@ export default { * Start playing a radio station and counting the playback * @param {Object} station Station object */ - doPlay(station) { + async doPlay(station) { const vm = this if (audioPlayer !== null) { @@ -201,6 +201,22 @@ export default { /* Count click */ axios.get(this.$apiUrl + '/json/url/' + station.stationuuid) + + /* Put into recent stations */ + try { + const stationMap = { + id: -1, + name: station.name, + urlresolved: station.url_resolved, + favicon: station.favicon, + stationuuid: station.stationuuid, + } + await axios + .post(generateUrl('/apps/radio/api/recent'), stationMap) + } catch (error) { + showError(t('radio', 'Could not add station to recent list')) + } + }, /** @@ -257,6 +273,8 @@ export default { queryURI = queryBase + '/byname/' + searchQuery } else if (menuState === 'FAVORITES') { queryURI = generateUrl('/apps/radio/api/favorites') + } else if (menuState === 'RECENT') { + queryURI = generateUrl('/apps/radio/api/recent') } await axios.get(queryURI, {