Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ captures/
*.apk
*.aab
*.keystore
node_modules/
/package-lock.json
/package.json
Binary file not shown.
Binary file added anixart-tizen/assets/fonts/proxima_nova_bold.otf
Binary file not shown.
Binary file added anixart-tizen/assets/fonts/roboto_medium.ttf
Binary file not shown.
Binary file not shown.
Binary file added anixart-tizen/assets/fonts/ytsans_medium.ttf
Binary file not shown.
Binary file added anixart-tizen/assets/icons/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added anixart-tizen/assets/icons/logo_splash.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added anixart-tizen/assets/icons/logo_splash_dark.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions anixart-tizen/config.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<widget xmlns="http://www.w3.org/ns/widgets" xmlns:tizen="http://tizen.org/ns/widgets" id="http://anixart.tizen.app" version="1.0.0" viewmodes="maximized">
<tizen:application id="anixart.tizen" package="anixart" required_version="6.0"/>
<content src="index.html"/>
<feature name="http://tizen.org/feature/screen.size.all"/>
<icon src="assets/icons/icon.png"/>
<name>Anixart</name>
<tizen:privilege name="http://tizen.org/privilege/internet"/>
<tizen:privilege name="http://tizen.org/privilege/tv.inputdevice"/>
<tizen:privilege name="http://tizen.org/privilege/application.launch"/>
<tizen:profile name="tv"/>
<tizen:setting screen-orientation="landscape" context-menu="enable" background-support="disable" encryption="disable" install-location="auto"/>
<access origin="https://api-s.anixsekai.com" subdomains="true"/>
<access origin="https://anixhelper.github.io" subdomains="true"/>
<access origin="*" subdomains="true"/>
</widget>
25 changes: 25 additions & 0 deletions anixart-tizen/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=1920, initial-scale=1.0">
<title>Anixart</title>
<link rel="stylesheet" href="styles/theme.css">
<link rel="stylesheet" href="styles/main.css">
</head>
<body>
<div id="app"></div>

<script src="src/services/storage.js"></script>
<script src="src/api/client.js"></script>
<script src="src/api/auth.js"></script>
<script src="src/api/discover.js"></script>
<script src="src/api/release.js"></script>
<script src="src/navigation/focus.js"></script>
<script src="src/components/bottom-nav.js"></script>
<script src="src/screens/login.js"></script>
<script src="src/screens/home.js"></script>
<script src="src/screens/details.js"></script>
<script src="src/app.js"></script>
</body>
</html>
10 changes: 10 additions & 0 deletions anixart-tizen/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "anixart-tizen",
"version": "1.0.0",
"description": "Anixart for Samsung Tizen TV",
"scripts": {
"build": "echo 'Static project — no build step required'",
"serve": "npx http-server . -p 8080 -c-1",
"package": "zip -r anixart-tizen.wgt config.xml index.html src/ styles/ assets/ -x '*.DS_Store'"
}
}
34 changes: 34 additions & 0 deletions anixart-tizen/src/api/auth.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
var AuthApi = {
STATUS_OK: 0,
STATUS_INVALID_LOGIN: 2,
STATUS_INVALID_PASSWORD: 3,

signIn: function(login, password) {
return ApiClient.post('auth/signIn', {
formData: {
login: login,
password: password
}
}).then(function(response) {
if (response.profileToken && response.profileToken.token) {
Storage.setToken(response.profileToken.token);
Storage.setTokenId(response.profileToken.id);
}
if (response.profile) {
Storage.setProfile(response.profile);
}
return response;
});
},

getErrorMessage: function(status) {
switch (status) {
case this.STATUS_INVALID_LOGIN:
return 'Неверная почта или никнейм';
case this.STATUS_INVALID_PASSWORD:
return 'Неверный пароль';
default:
return 'Ошибка входа';
}
}
};
75 changes: 75 additions & 0 deletions anixart-tizen/src/api/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
var ApiClient = {
BASE_URL: 'https://api-s.anixsekai.com/',

request: function(method, endpoint, options) {
options = options || {};
var url = this.BASE_URL + endpoint;

if (options.token) {
url += (url.indexOf('?') === -1 ? '?' : '&') + 'token=' + encodeURIComponent(options.token);
}

if (options.queryParams) {
for (var key in options.queryParams) {
if (options.queryParams.hasOwnProperty(key)) {
url += (url.indexOf('?') === -1 ? '?' : '&') + encodeURIComponent(key) + '=' + encodeURIComponent(options.queryParams[key]);
}
}
}

var xhr = new XMLHttpRequest();
xhr.open(method, url, true);

if (options.formData) {
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
} else if (options.json) {
xhr.setRequestHeader('Content-Type', 'application/json');
}

return new Promise(function(resolve, reject) {
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
try {
resolve(JSON.parse(xhr.responseText));
} catch (e) {
resolve(xhr.responseText);
}
} else {
reject({ status: xhr.status, text: xhr.responseText });
}
};

xhr.onerror = function() {
reject({ status: 0, text: 'Network error' });
};

xhr.ontimeout = function() {
reject({ status: 0, text: 'Request timeout' });
};

xhr.timeout = 15000;

if (options.formData) {
var parts = [];
for (var key in options.formData) {
if (options.formData.hasOwnProperty(key)) {
parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(options.formData[key]));
}
}
xhr.send(parts.join('&'));
} else if (options.json) {
xhr.send(JSON.stringify(options.json));
} else {
xhr.send();
}
});
},

post: function(endpoint, options) {
return this.request('POST', endpoint, options);
},

get: function(endpoint, options) {
return this.request('GET', endpoint, options);
}
};
28 changes: 28 additions & 0 deletions anixart-tizen/src/api/discover.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
var DiscoverApi = {
getInteresting: function() {
return ApiClient.post('discover/interesting');
},

getRecommendations: function(page, previousPage, token) {
return ApiClient.post('discover/recommendations/' + page, {
token: token,
queryParams: { previous_page: previousPage }
});
},

getWatching: function(page, token) {
return ApiClient.post('discover/watching/' + page, {
token: token
});
},

getDiscussing: function(token) {
return ApiClient.post('discover/discussing', {
token: token
});
},

getComments: function() {
return ApiClient.post('discover/comments');
}
};
20 changes: 20 additions & 0 deletions anixart-tizen/src/api/release.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
var ReleaseApi = {
getRelease: function(releaseId, token) {
return ApiClient.post('release/' + releaseId, {
token: token
});
},

getEpisodes: function(releaseId, sourceId, token) {
return ApiClient.post('release/' + releaseId + '/episode', {
token: token,
queryParams: sourceId ? { sourceId: sourceId } : undefined
});
},

getSources: function(releaseId, token) {
return ApiClient.post('release/' + releaseId + '/source', {
token: token
});
}
};
75 changes: 75 additions & 0 deletions anixart-tizen/src/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
var App = {
history: [],
currentScreen: null,

init: function() {
document.documentElement.setAttribute('data-theme', Storage.getTheme());

FocusManager.init();

if (typeof tizen !== 'undefined') {
try {
tizen.tvinputdevice.registerKeyBatch([
'MediaPlay', 'MediaPause', 'MediaPlayPause',
'MediaStop', 'MediaFastForward', 'MediaRewind'
]);
} catch (e) {}
}

if (Storage.isLoggedIn()) {
this.showScreen('home');
} else {
this.showScreen('login');
}
},

showScreen: function(name, params) {
if (this.currentScreen && this.currentScreen !== name) {
this.history.push({ name: this.currentScreen, params: this.currentParams });
}
this.currentScreen = name;
this.currentParams = params;

switch (name) {
case 'login':
LoginScreen.render();
break;
case 'home':
HomeScreen.render();
break;
case 'details':
DetailsScreen.render(params);
break;
}
},

goBack: function() {
if (this.history.length > 0) {
var prev = this.history.pop();
this.currentScreen = prev.name;
this.currentParams = prev.params;
switch (prev.name) {
case 'login':
LoginScreen.render();
break;
case 'home':
HomeScreen.render();
break;
case 'details':
DetailsScreen.render(prev.params);
break;
}
} else if (this.currentScreen !== 'home' && Storage.isLoggedIn()) {
this.currentScreen = 'home';
HomeScreen.render();
} else {
if (typeof tizen !== 'undefined') {
tizen.application.getCurrentApplication().exit();
}
}
}
};

document.addEventListener('DOMContentLoaded', function() {
App.init();
});
52 changes: 52 additions & 0 deletions anixart-tizen/src/components/bottom-nav.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
var BottomNav = {
tabs: [
{ id: 'home', label: 'Главная', icon: '<svg width="24" height="24" viewBox="0 0 24 24"><path d="M10 20v-6h4v6h5v-8h3L12 3 2 12h3v8z" fill="currentColor"/></svg>' },
{ id: 'discover', label: 'Обзор', icon: '<svg width="24" height="24" viewBox="0 0 24 24"><path d="M12 10.9c-.61 0-1.1.49-1.1 1.1s.49 1.1 1.1 1.1c.61 0 1.1-.49 1.1-1.1s-.49-1.1-1.1-1.1zM12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm2.19 12.19L6 18l3.81-8.19L18 6l-3.81 8.19z" fill="currentColor"/></svg>' },
{ id: 'bookmarks', label: 'Закладки', icon: '<svg width="24" height="24" viewBox="0 0 24 24"><path d="M17 3H7c-1.1 0-2 .9-2 2v16l7-3 7 3V5c0-1.1-.9-2-2-2z" fill="currentColor"/></svg>' },
{ id: 'feed', label: 'Лента', icon: '<svg width="24" height="24" viewBox="0 0 24 24"><path d="M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z" fill="currentColor"/></svg>' },
{ id: 'profile', label: 'Профиль', icon: '<svg width="24" height="24" viewBox="0 0 24 24"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z" fill="currentColor"/></svg>' }
],

render: function(activeTab) {
var nav = document.createElement('div');
nav.className = 'bottom-nav';

for (var i = 0; i < this.tabs.length; i++) {
var tab = this.tabs[i];
var btn = document.createElement('button');
btn.className = 'nav-tab' + (tab.id === activeTab ? ' active' : '');
btn.setAttribute('data-focusable', 'true');
btn.setAttribute('data-tab', tab.id);

var indicator = document.createElement('div');
indicator.className = 'nav-indicator';

var iconWrap = document.createElement('span');
iconWrap.className = 'nav-icon';
iconWrap.innerHTML = tab.icon;
indicator.appendChild(iconWrap);
btn.appendChild(indicator);

var label = document.createElement('span');
label.className = 'nav-label';
label.textContent = tab.label;
btn.appendChild(label);

(function(tabId) {
btn.addEventListener('click', function() {
BottomNav.onTabClick(tabId);
});
})(tab.id);

nav.appendChild(btn);
}

return nav;
},

onTabClick: function(tabId) {
if (tabId === 'home') {
App.showScreen('home');
}
}
};
Loading
Loading