// Variables used by Scriptable. // These must be at the very top of the file. Do not edit. // icon-color: deep-green; icon-glyph: angle-double-right; // // iOS 桌面组件脚本 @「小件件」 // 开发说明:请从 Widget 类开始编写,注释请勿修改 // https://x.im3x.cn // // 添加require,是为了vscode中可以正确引入包,以获得自动补全等功能 if (typeof require === 'undefined') require = importModule const { Base } = require("./「小件件」开发环境") // @组件代码开始 class Widget extends Base { constructor (arg) { super(arg) this.logo = "https://www.v2ex.com/static/img/icon_rayps_64.png" this.name = "V2EX" this.desc = "创意工作者们的社区" // 请求数据接口列表(收集整理中) this.API = [ // api [ { id: 'latest', name: '最新' }, { id: 'hot', name: '最热' } ], // tab [ { id: 'all', name: '全部' }, { id: 'hot', name: '最热' }, { id: 'tech', name: '技术' }, { id: 'creative', name: '创意' }, { id: 'playplay', name: '好玩' }, { id: 'apple', name: 'Apple' }, { id: 'jobs', name: '酷工作' }, { id: 'deals', name: '交易' }, { id: 'city', name: '城市' }, { id: 'qna', name: '问与答' } ], // go [], ] // 当前设置的存储key(提示:可通过桌面设置不同参数,来保存多个设置) let _md5 = this.md5(module.filename) this.CACHE_KEY = `cache_${_md5}` // 获取设置 // 格式:type@name,比如 go@create、api@hot、tab@all this.SETTINGS = this.settings['node'] || 'tab@all' // 注册操作菜单 this.registerAction("节点设置", this.actionSettings) } // 渲染组件 async render () { // 加载节点列表 await this._loadNodes() const data = await this.getData() console.log(data) if (this.widgetFamily === 'medium') { return await this.renderMedium(data) } else if (this.widgetFamily === 'large') { return await this.renderLarge(data) } else { return await this.renderSmall(data) } } async renderSmall (data) { let w = new ListWidget() let topic = data[0] w.url = this.actionUrl('open-url', topic['url']) w = await this.renderHeader(w, this.logo, this.name + ' / ' + this.SETTINGS.split('@')[0], Color.white()) let content = w.addText(topic['title']) content.font = Font.lightSystemFont(16) content.textColor = Color.white() content.lineLimit = 3 w.backgroundImage = await this.shadowImage(await this.getImageByUrl(topic['member']['avatar_large'].replace('mini', 'large'))) w.addSpacer() let footer = w.addText(`@${topic['member']['username']} / ${topic['node']['title']}`) footer.font = Font.lightSystemFont(10) footer.textColor = Color.white() footer.textOpacity = 0.5 footer.lineLimit = 1 return w } // 中尺寸组件 async renderMedium (data) { let w = new ListWidget() // w.addSpacer(10) // 设置名称 let tmp = this.SETTINGS.split('@') let tid = tmp[0] === 'api' ? 0 : (tmp[0] === 'tab' ? 1 : 2) let current = '' this.API[tid].map(a => { if (a['id'] === tmp[1]) current = a['name'] }) await this.renderHeader(w, this.logo, this.name + ' / ' + current) w.addSpacer() let body = w.addStack() let bodyleft= body.addStack() bodyleft.layoutVertically() for (let i = 0; i < 2; i ++) { bodyleft = await this.renderCell(bodyleft, data[i]) bodyleft.addSpacer() } // body.addSpacer() w.url = this.actionUrl("settings") return w } // 大尺寸组件 async renderLarge (data) { let w = new ListWidget() // w.addSpacer(10) // 设置名称 let tmp = this.SETTINGS.split('@') let tid = tmp[0] === 'api' ? 0 : (tmp[0] === 'tab' ? 1 : 2) let current = '' this.API[tid].map(a => { if (a['id'] === tmp[1]) current = a['name'] }) await this.renderHeader(w, this.logo, this.name + ' / ' + current) w.addSpacer() let body = w.addStack() let bodyleft= body.addStack() bodyleft.layoutVertically() for (let i = 0; i < 5; i ++) { bodyleft = await this.renderCell(bodyleft, data[i]) bodyleft.addSpacer() } // body.addSpacer() // w.addSpacer() w.url = this.actionUrl("settings") return w } async renderCell (widget, topic) { let body = widget.addStack() body.url = this.actionUrl('open-url', topic['url']) let left = body.addStack() let avatar = left.addImage(await this.getImageByUrl(topic['member']['avatar_large'].replace('mini', 'large'))) avatar.imageSize = new Size(35, 35) avatar.cornerRadius = 5 body.addSpacer(10) let right = body.addStack() right.layoutVertically() let content = right.addText(topic['title']) content.font = Font.lightSystemFont(14) content.lineLimit = 1 right.addSpacer(5) let info = right.addText(`@${topic['member']['username']} / ${topic['node']['title']}`) info.font = Font.lightSystemFont(10) info.textOpacity = 0.6 info.lineLimit = 2 widget.addSpacer() return widget } async getData () { // 解析设置,判断类型,获取对应数据 const tmp = this.SETTINGS.split('@') switch (tmp[0]) { case 'tab': return await this.getDataForTab(tmp[1]) case 'go': return await this.getDataForGo(tmp[1]) case 'api': return await this.getDataForApi(tmp[1]) } } /** * 获取首页tab数据 * @param {string} tab tab首页名称 */ async getDataForTab (tab = 'all') { let url = `https://www.v2ex.com/?tab=${tab}` let html = await this.fetchAPI(url, false) // 解析html let tmp = html.split(`