-
Notifications
You must be signed in to change notification settings - Fork 352
app object
This page shows examples of the current app object API. First we show how to open a window:
var window = app.createWindow({
width : 1024,
height : 768,
icons : __dirname + '/content/icons', //used for application window icon
showChrome : false, //display as standard os window with max/min/close buttons
alpha: true,
autoResize: false, //adjust window size automatically according to content.
resizable: true, //allow user resize of window
margin: 0,
disableSecurity:true, //turn off security restrictions.
showResizeGrip:false, //show/hide the resize grip.
name:'test', //undocumented parameter
left:-1,
top:-1,
opacity:1,
fullscreen:false,
topmost:false,
/***************************** defaults ********************************
* url : 'http://appjs', // serve static file root and routers
* autoResize : false, // resizes in response to html content
* showChrome : true, // show border and title bar
* resizable : false, // control if users can resize window
* disableSecurity: true, // allow cross origin requests
* opacity : 1, // flat percent opacity for window
* alpha : false, // per-pixel alpha blended (Win & Mac)
* fullscreen : false, // client area covers whole screen
* left : -1, // centered by default
* top : -1, // centered by default
*************************************************************************/
});
Following code gives an example of creating a native menu. Use & to create a shortcut key, so &File will cause Alt+F to select the File menu item on windows, other OS use the appropriate key plus the letter after the & to select it:
var menubar = app.createMenu([{
label:'&File',
submenu:[
{
label:'E&xit',
action: function(){
window.close();
}
}
]
},{
label:'&Window',
submenu:[
{
label:'Fullscreen',
action:function(item) {
window.frame.fullscreen();
console.log(item.label+" called.");
}
},
{
label:'Minimize',
action:function(){
window.frame.minimize();
}
},
{
label:'Maximize',
action:function(){
window.frame.maximize();
}
},{
label:''//separator
},{
label:'Restore',
action:function(){
window.frame.restore();
}
}
]
}]);
menubar.on('select',function(item){
console.log("menu item "+item.label+" clicked");
});
// don't forget to attach it to the window!
var window = app.createWindow({
width : 640,
height : 460,
icons : __dirname + '/content/icons'
});
window.on('create', function(){
console.log("Window Created");
window.frame.show();
window.frame.center();
window.frame.setMenuBar(menubar); // attaching menubar to a new window.
});
Create a tray menu to be displayed in the status bar:
var trayMenu = app.createMenu([{
label:'Show',
action:function(){
window.frame.show();
},
},{
label:'Minimize',
action:function(){
window.frame.hide();
}
},{
label:'Exit',
action:function(){
window.close();
}
}]);
Display a status icon and attach the trayMenu to it.
var statusIcon = app.createStatusIcon({
icon:'./data/content/icons/32.png',
tooltip:'AppJS Hello World',
menu:trayMenu
});
import React, { useMemo, useState, useCallback } from "react"; import { SafeAreaView, View, Text, FlatList, TouchableOpacity, StyleSheet, StatusBar, } from "react-native";
// ----------------------------- // Datos de ejemplo Saufy TV // ----------------------------- const VIDEOS = [ { id: "1", titulo: "Juventud y liderazgo en Ecuador", descripcion: "Conversación sobre liderazgo juvenil, política y participación ciudadana en Ecuador.", tags: ["juventud", "liderazgo", "ecuador", "politica"], duracionMin: 42, fechaPublicacion: "2025-11-10", viewsTotales: 1250, likesTotales: 210, tipo: "podcast_largo", invitado: "Líder juvenil AIESEC", }, { id: "2", titulo: "Historias desde el pupito del mundo", descripcion: "Relatos íntimos de vida, resiliencia y sueños contados desde los buses y las calles de Quito.", tags: ["historias", "vida", "quito", "emocional"], duracionMin: 35, fechaPublicacion: "2025-11-15", viewsTotales: 890, likesTotales: 150, tipo: "podcast_largo", invitado: "Saúl Chisaguano", }, { id: "3", titulo: "Sexo, cuerpo y tabúes: hablemos sin miedo", descripcion: "Episodio sobre educación sexual, tabúes y cómo comunicarnos con responsabilidad.", tags: ["sexualidad", "cuerpo", "tabu", "educacion"], duracionMin: 55, fechaPublicacion: "2025-11-12", viewsTotales: 1670, likesTotales: 320, tipo: "podcast_largo", invitado: "Invitada especialista", }, { id: "4", titulo: "Clip: ¿Qué es ser líder en 30 segundos?", descripcion: "Clip corto sobre la esencia del liderazgo en contextos juveniles.", tags: ["liderazgo", "juventud", "clip"], duracionMin: 1, fechaPublicacion: "2025-11-16", viewsTotales: 3200, likesTotales: 680, tipo: "clip", invitado: "Líder juvenil AIESEC", }, { id: "5", titulo: "Emprender desde cero con 2 maletas", descripcion: "Historia de emprendimiento, fracasos y comenzar de nuevo con casi nada.", tags: ["emprendimiento", "historias", "motivacion"], duracionMin: 48, fechaPublicacion: "2025-11-08", viewsTotales: 980, likesTotales: 190, tipo: "podcast_largo", invitado: "Emprendedor invitado", }, { id: "6", titulo: "Clip: Un consejo para no rendirte hoy", descripcion: "Mensaje corto para quienes están a punto de rendirse con sus proyectos.", tags: ["motivacion", "clip", "vida"], duracionMin: 2, fechaPublicacion: "2025-11-17", viewsTotales: 4100, likesTotales: 910, tipo: "clip", invitado: "Saúl Chisaguano", }, ];
// Usuario simulado (luego esto se puede conectar a auth real) const USUARIO_INICIAL = { id: "user-1", nombre: "Saúl", interesesIniciales: [ "juventud", "liderazgo", "historias", "emprendimiento", "sexualidad", ], };
// ----------------------------- // Helpers // -----------------------------
function diasDesde(fechaStr) { const hoy = new Date(); const fecha = new Date(fechaStr); const diffMs = hoy.getTime() - fecha.getTime(); return Math.floor(diffMs / (1000 * 60 * 60 * 24)); }
function normalizarCampo(lista, campo) { const valores = lista.map((v) => v[campo]); const max = Math.max(...valores); const min = Math.min(...valores); if (max === min) { return {}; // todos iguales, no aporta } const resultado = {}; lista.forEach((v) => { resultado[v.id] = (v[campo] - min) / (max - min); }); return resultado; }
// ----------------------------- // Lógica de recomendación // -----------------------------
function obtenerInteresesDesdeHistorial(historial, videos) { const contadorTags = {}; historial.forEach((item) => { const video = videos.find((v) => v.id === item.idVideo); if (!video) return; video.tags.forEach((tag) => { if (!contadorTags[tag]) contadorTags[tag] = 0; contadorTags[tag] += 1; }); });
const pares = Object.entries(contadorTags); pares.sort((a, b) => b[1] - a[1]); // orden descendente return pares.slice(0, 5).map(([tag]) => tag); }
function recomendarVideos(usuario, historial, videos) { const vistos = new Set(historial.map((h) => h.idVideo)); const interesesUsuario = historial.length > 0 ? obtenerInteresesDesdeHistorial(historial, videos) : usuario.interesesIniciales;
const viewsNorm = normalizarCampo(videos, "viewsTotales"); const likesNorm = normalizarCampo(videos, "likesTotales");
const puntuados = videos.map((video) => { let score = 0;
// 1. Coincidencia con intereses
const tagsEnComun = video.tags.filter((t) =>
interesesUsuario.includes(t)
).length;
score += tagsEnComun * 3;
// 2. Popularidad
const vNorm = viewsNorm[video.id] ?? 0;
const lNorm = likesNorm[video.id] ?? 0;
score += vNorm * 2;
score += lNorm * 2;
// 3. Novedad
const dias = diasDesde(video.fechaPublicacion);
if (dias < 3) score += 5;
else if (dias < 7) score += 3;
else if (dias < 30) score += 1;
// 4. Afinidad por invitado en historial
const haVistoMismoInvitado = historial.some((h) => {
const vHist = videos.find((v) => v.id === h.idVideo);
return vHist && vHist.invitado === video.invitado;
});
if (haVistoMismoInvitado) score += 4;
// 5. Penalizar si ya visto casi completo (simulado con flag)
const r